Merge pull request #35 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2021-02-16 18:09:10 +03:00 committed by GitHub
commit 618365754e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 684 additions and 520 deletions

View File

@ -17,7 +17,7 @@ insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4
continuation_indent_size = 8
continuation_indent_size = 4
[*.xml]
continuation_indent_size = 4

2
.github/FUNDING.yml vendored
View File

@ -1,3 +1,3 @@
github: [tibbi]
patreon: tiborkaputa
custom: ["https://www.paypal.me/SimpleMobileTools", "https://www.simplemobiletools.com/donate"]
custom: ["https://www.paypal.com/paypalme/simplemobiletools", "https://www.simplemobiletools.com/donate"]

View File

@ -1,6 +1,28 @@
Changelog
==========
Version 6.14.1 *(2021-02-15)*
----------------------------
* Allow customizing the ringtone per contact (Ringtone has to be enabled as a customizable field in the app settings)
* Added a White theme with special handling
* Added some letter fastscroller related improvements
* Some performance, stability and translation related improvements
* Please rate the app if you like it :)
Version 6.14.0 *(2021-01-10)*
----------------------------
* Always fetch the same contacts, no matter what sorting is set
* Fix an organization updating related issue
* Merge names into 1 line at the View Details screen
* Improve Threema contact handling
Version 6.13.2 *(2021-01-02)*
----------------------------
* Added some translation, stability and UX improvements
Version 6.13.1 *(2020-10-27)*
----------------------------

View File

@ -13,7 +13,7 @@ This app is just one piece of a bigger series of apps. You can find the rest of
<a href='https://f-droid.org/packages/com.simplemobiletools.contacts.pro'><img src='https://simplemobiletools.com/assets/images/f-droid.png' alt='Get it on F-Droid' height='45' /></a>
<div style="display:flex;">
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg" width="30%">
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg" width="30%">
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg" width="30%">
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/english/1.jpg" width="30%">
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/english/2.jpg" width="30%">
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/english/3.jpg" width="30%">
</div>

View File

@ -17,8 +17,8 @@ android {
applicationId "com.simplemobiletools.contacts.pro"
minSdkVersion 21
targetSdkVersion 29
versionCode 77
versionName "6.13.1"
versionCode 80
versionName "6.14.1"
setProperty("archivesBaseName", "contacts")
}
@ -57,13 +57,13 @@ android {
}
dependencies {
implementation 'com.simplemobiletools:commons:5.31.18'
implementation 'com.simplemobiletools:commons:5.33.30'
implementation 'joda-time:joda-time:2.10.1'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.5'
implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a'
implementation 'com.github.tibbi:IndicatorFastScroll:c3de1d040a'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
kapt "androidx.room:room-compiler:2.2.5"
implementation "androidx.room:room-runtime:2.2.5"
annotationProcessor "androidx.room:room-compiler:2.2.5"
kapt "androidx.room:room-compiler:2.2.6"
implementation "androidx.room:room-runtime:2.2.6"
annotationProcessor "androidx.room:room-compiler:2.2.6"
}

View File

@ -1,11 +1,13 @@
package com.simplemobiletools.contacts.pro.activities
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.provider.ContactsContract.CommonDataKinds.*
import android.widget.ImageView
import android.widget.TextView
@ -24,16 +26,25 @@ import com.simplemobiletools.commons.helpers.letterBackgroundColors
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.extensions.sendEmailIntent
import com.simplemobiletools.contacts.pro.extensions.sendSMSIntent
import com.simplemobiletools.contacts.pro.extensions.shareContacts
import com.simplemobiletools.contacts.pro.helpers.ContactsHelper
import com.simplemobiletools.contacts.pro.models.Contact
import java.util.*
abstract class ContactActivity : SimpleActivity() {
protected val PICK_RINGTONE_INTENT_ID = 1500
protected var contact: Contact? = null
protected var currentContactPhotoPath = ""
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
super.onActivityResult(requestCode, resultCode, resultData)
if (requestCode == PICK_RINGTONE_INTENT_ID && resultCode == RESULT_OK && resultData != null && resultData.dataString != null) {
customRingtoneSelected(Uri.decode(resultData.dataString!!))
}
}
abstract fun customRingtoneSelected(ringtonePath: String)
fun showPhotoPlaceholder(photoView: ImageView) {
val placeholder = BitmapDrawable(resources, getBigLetterPlaceholder(contact?.getNameToDisplay() ?: "A"))
photoView.setImageDrawable(placeholder)
@ -92,7 +103,7 @@ abstract class ContactActivity : SimpleActivity() {
fun trySendSMS() {
val numbers = contact!!.phoneNumbers
if (numbers.size == 1) {
sendSMSIntent(numbers.first().value)
launchSendSMSIntent(numbers.first().value)
} else if (numbers.size > 1) {
val items = ArrayList<RadioItem>()
numbers.forEachIndexed { index, phoneNumber ->
@ -100,7 +111,7 @@ abstract class ContactActivity : SimpleActivity() {
}
RadioGroupDialog(this, items) {
sendSMSIntent(it as String)
launchSendSMSIntent(it as String)
}
}
}
@ -189,6 +200,7 @@ abstract class ContactActivity : SimpleActivity() {
isAntiAlias = true
textAlign = Paint.Align.CENTER
textSize = wantedTextSize
style = Paint.Style.FILL
}
canvas.drawPaint(circlePaint)

View File

@ -5,8 +5,8 @@ import android.content.ClipData
import android.content.ContentValues
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.media.AudioManager
import android.media.RingtoneManager
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract.CommonDataKinds
@ -20,6 +20,7 @@ import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.RadioItem
@ -184,6 +185,7 @@ class EditContactActivity : ContactActivity() {
}
setupTypePickers()
setupRingtone()
if (contact!!.photoUri.isEmpty() && contact!!.photo == null) {
showPhotoPlaceholder(contact_photo)
@ -194,7 +196,7 @@ class EditContactActivity : ContactActivity() {
val textColor = config.textColor
arrayOf(contact_name_image, contact_numbers_image, contact_emails_image, contact_addresses_image, contact_ims_image, contact_events_image,
contact_notes_image, contact_organization_image, contact_websites_image, contact_groups_image, contact_source_image).forEach {
contact_notes_image, contact_ringtone_image, contact_organization_image, contact_websites_image, contact_groups_image, contact_source_image).forEach {
it.applyColorFilter(textColor)
}
@ -380,6 +382,10 @@ class EditContactActivity : ContactActivity() {
val areNotesVisible = showFields and SHOW_NOTES_FIELD != 0
contact_notes.beVisibleIf(areNotesVisible)
contact_notes_image.beVisibleIf(areNotesVisible)
val isRingtoneVisible = showFields and SHOW_RINGTONE_FIELD != 0
contact_ringtone.beVisibleIf(isRingtoneVisible)
contact_ringtone_image.beVisibleIf(isRingtoneVisible)
}
private fun setupEditContact() {
@ -480,6 +486,31 @@ class EditContactActivity : ContactActivity() {
contact_notes.setText(contact!!.notes)
}
private fun setupRingtone() {
contact_ringtone.setOnClickListener {
val currentRingtone = contact!!.ringtone ?: getDefaultAlarmSound(RingtoneManager.TYPE_RINGTONE).uri
SelectAlarmSoundDialog(this, currentRingtone, AudioManager.STREAM_RING, PICK_RINGTONE_INTENT_ID, RingtoneManager.TYPE_RINGTONE, true,
onAlarmPicked = {
contact!!.ringtone = it?.uri
contact_ringtone.text = it?.title
}, onAlarmSoundDeleted = {}
)
}
val ringtone = contact!!.ringtone
if (ringtone != null && ringtone.isNotEmpty()) {
if (ringtone == SILENT) {
contact_ringtone.text = getString(R.string.no_sound)
} else {
val contactRingtone = RingtoneManager.getRingtone(this, Uri.parse(ringtone))
contact_ringtone.text = contactRingtone.getTitle(this)
}
} else {
val default = getDefaultAlarmSound(RingtoneManager.TYPE_RINGTONE)
contact_ringtone.text = default.title
}
}
private fun setupOrganization() {
contact_organization_company.setText(contact!!.organization.company)
contact_organization_job_position.setText(contact!!.organization.jobPosition)
@ -1211,6 +1242,11 @@ class EditContactActivity : ContactActivity() {
}
}
override fun customRingtoneSelected(ringtonePath: String) {
contact!!.ringtone = ringtonePath
contact_ringtone.text = ringtonePath.getFilenameFromPath()
}
private fun getPhoneNumberTypeId(value: String) = when (value) {
getString(R.string.mobile) -> Phone.TYPE_MOBILE
getString(R.string.home) -> Phone.TYPE_HOME

View File

@ -59,9 +59,6 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private var isGettingContacts = false
private var ignoredExportContactSources = HashSet<String>()
private var storedTextColor = 0
private var storedBackgroundColor = 0
private var storedPrimaryColor = 0
private var storedShowContactThumbnails = false
private var storedShowPhoneNumbers = false
private var storedStartNameWithSurname = false
@ -114,29 +111,14 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
}
val configTextColor = config.textColor
if (storedTextColor != configTextColor) {
getInactiveTabIndexes(viewpager.currentItem).forEach {
main_tabs_holder.getTabAt(it)?.icon?.applyColorFilter(configTextColor)
}
getAllFragments().forEach {
it?.textColorChanged(configTextColor)
}
val adjustedPrimaryColor = getAdjustedPrimaryColor()
main_tabs_holder.background = ColorDrawable(config.backgroundColor)
main_tabs_holder.setSelectedTabIndicatorColor(adjustedPrimaryColor)
getAllFragments().forEach {
it?.setupColors(config.textColor, adjustedPrimaryColor)
}
val configBackgroundColor = config.backgroundColor
if (storedBackgroundColor != configBackgroundColor) {
main_tabs_holder.background = ColorDrawable(configBackgroundColor)
}
val configPrimaryColor = config.primaryColor
if (storedPrimaryColor != configPrimaryColor) {
main_tabs_holder.setSelectedTabIndicatorColor(getAdjustedPrimaryColor())
main_tabs_holder.getTabAt(viewpager.currentItem)?.icon?.applyColorFilter(getAdjustedPrimaryColor())
getAllFragments().forEach {
it?.primaryColorChanged()
}
}
updateTabColors()
val configStartNameWithSurname = config.startNameWithSurname
if (storedStartNameWithSurname != configStartNameWithSurname) {
@ -156,17 +138,13 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
initFragments()
} else {
refreshContacts(ALL_TABS_MASK)
getAllFragments().forEach {
it?.onActivityResume()
}
}
}
val dialpadIcon = resources.getColoredDrawableWithColor(R.drawable.ic_dialpad_vector, getFABIconColor())
val dialpadIcon = resources.getColoredDrawableWithColor(R.drawable.ic_dialpad_vector, adjustedPrimaryColor.getContrastColor())
main_dialpad_button.apply {
setImageDrawable(dialpadIcon)
background.applyColorFilter(getAdjustedPrimaryColor())
background.applyColorFilter(adjustedPrimaryColor)
beVisibleIf(config.showDialpadButton)
}
@ -233,9 +211,6 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private fun storeStateVariables() {
config.apply {
storedTextColor = textColor
storedBackgroundColor = backgroundColor
storedPrimaryColor = primaryColor
storedShowContactThumbnails = showContactThumbnails
storedShowPhoneNumbers = showPhoneNumbers
storedStartNameWithSurname = startNameWithSurname
@ -280,6 +255,13 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
})
}
private fun updateTabColors() {
getInactiveTabIndexes(viewpager.currentItem).forEach {
main_tabs_holder.getTabAt(it)?.icon?.applyColorFilter(config.textColor)
}
main_tabs_holder.getTabAt(viewpager.currentItem)?.icon?.applyColorFilter(getAdjustedPrimaryColor())
}
private fun getSearchString(): Int {
return when (getCurrentFragment()) {
favorites_fragment -> R.string.search_favorites
@ -528,7 +510,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
private fun exportContactsTo(ignoredContactSources: HashSet<String>, outputStream: OutputStream?) {
ContactsHelper(this).getContacts(true, ignoredContactSources) { contacts ->
ContactsHelper(this).getContacts(true, false, ignoredContactSources) { contacts ->
if (contacts.isEmpty()) {
toast(R.string.no_entries_for_exporting)
} else {
@ -567,6 +549,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
if (viewpager.adapter == null) {
viewpager.adapter = ViewPagerAdapter(this, tabsList, config.showTabs)
viewpager.currentItem = getDefaultTab()
updateTabColors()
}
ContactsHelper(this).getContacts { contacts ->

View File

@ -2,19 +2,18 @@ package com.simplemobiletools.contacts.pro.activities
import android.content.ContentUris
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.media.AudioManager
import android.media.RingtoneManager
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
import android.view.View
import android.view.WindowManager
import android.widget.RelativeLayout
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.CONTACT_ID
import com.simplemobiletools.commons.helpers.IS_PRIVATE
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.dialogs.CallConfirmationDialog
import com.simplemobiletools.contacts.pro.dialogs.ChooseSocialDialog
@ -175,7 +174,7 @@ class ViewContactActivity : ContactActivity() {
val textColor = config.textColor
arrayOf(contact_name_image, contact_numbers_image, contact_emails_image, contact_addresses_image, contact_events_image, contact_source_image,
contact_notes_image, contact_organization_image, contact_websites_image, contact_groups_image).forEach {
contact_notes_image, contact_ringtone_image, contact_organization_image, contact_websites_image, contact_groups_image).forEach {
it.applyColorFilter(textColor)
}
@ -217,6 +216,7 @@ class ViewContactActivity : ContactActivity() {
setupGroups()
setupContactSources()
setupNotes()
setupRingtone()
setupOrganization()
updateTextColors(contact_scrollview)
}
@ -255,36 +255,11 @@ class ViewContactActivity : ContactActivity() {
}
private fun setupNames() {
contact!!.apply {
contact_prefix.text = prefix
contact_prefix.beVisibleIf(prefix.isNotEmpty() && showFields and SHOW_PREFIX_FIELD != 0)
contact_prefix.copyOnLongClick(prefix)
contact_first_name.text = firstName
contact_first_name.beVisibleIf(firstName.isNotEmpty() && showFields and SHOW_FIRST_NAME_FIELD != 0)
contact_first_name.copyOnLongClick(firstName)
contact_middle_name.text = middleName
contact_middle_name.beVisibleIf(middleName.isNotEmpty() && showFields and SHOW_MIDDLE_NAME_FIELD != 0)
contact_middle_name.copyOnLongClick(middleName)
contact_surname.text = surname
contact_surname.beVisibleIf(surname.isNotEmpty() && showFields and SHOW_SURNAME_FIELD != 0)
contact_surname.copyOnLongClick(surname)
contact_suffix.text = suffix
contact_suffix.beVisibleIf(suffix.isNotEmpty() && showFields and SHOW_SUFFIX_FIELD != 0)
contact_suffix.copyOnLongClick(suffix)
contact_nickname.text = nickname
contact_nickname.beVisibleIf(nickname.isNotEmpty() && showFields and SHOW_NICKNAME_FIELD != 0)
contact_nickname.copyOnLongClick(nickname)
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()
}
}
val displayName = contact!!.getNameToDisplay()
contact_name.text = displayName
contact_name.copyOnLongClick(displayName)
contact_name.beVisibleIf(displayName.isNotEmpty() && !contact!!.isABusinessContact())
contact_name_image.beInvisibleIf(contact_name.isGone())
}
private fun setupPhoneNumbers() {
@ -564,6 +539,14 @@ class ViewContactActivity : ContactActivity() {
showSocialActions(key.id)
}
}
if (value.toLowerCase() == THREEMA) {
contact_source_image.setImageDrawable(getPackageDrawable(THREEMA_PACKAGE))
contact_source_image.beVisible()
contact_source_image.setOnClickListener {
showSocialActions(key.id)
}
}
}
}
@ -588,6 +571,43 @@ class ViewContactActivity : ContactActivity() {
}
}
private fun setupRingtone() {
if (showFields and SHOW_RINGTONE_FIELD != 0) {
contact_ringtone_image.beVisible()
contact_ringtone.beVisible()
val ringtone = contact!!.ringtone
if (ringtone != null && ringtone.isNotEmpty()) {
if (ringtone == SILENT) {
contact_ringtone.text = getString(R.string.no_sound)
} else {
val contactRingtone = RingtoneManager.getRingtone(this, Uri.parse(ringtone))
val ringtoneTitle = contactRingtone.getTitle(this)
contact_ringtone.text = ringtoneTitle
}
} else {
contact_ringtone_image.beGone()
contact_ringtone.beGone()
return
}
contact_ringtone.copyOnLongClick(contact_ringtone.text.toString())
contact_ringtone.setOnClickListener {
val currentRingtone = contact!!.ringtone ?: getDefaultAlarmSound(RingtoneManager.TYPE_RINGTONE).uri
SelectAlarmSoundDialog(this, currentRingtone, AudioManager.STREAM_RING, PICK_RINGTONE_INTENT_ID, RingtoneManager.TYPE_RINGTONE, true,
onAlarmPicked = {
contact_ringtone.text = it?.title
ringtoneUpdated(it?.uri)
}, onAlarmSoundDeleted = {}
)
}
} else {
contact_ringtone_image.beGone()
contact_ringtone.beGone()
}
}
private fun setupOrganization() {
val organization = contact!!.organization
if (organization.isNotEmpty() && showFields and SHOW_ORGANIZATION_FIELD != 0) {
@ -625,6 +645,23 @@ class ViewContactActivity : ContactActivity() {
}
}
override fun customRingtoneSelected(ringtonePath: String) {
contact_ringtone.text = ringtonePath.getFilenameFromPath()
ringtoneUpdated(ringtonePath)
}
private fun ringtoneUpdated(path: String?) {
contact!!.ringtone = path
ensureBackgroundThread {
if (contact!!.isPrivate()) {
LocalContactsHelper(this).updateRingtone(contact!!.contactId, path ?: "")
} else {
ContactsHelper(this).updateRingtone(contact!!.contactId.toString(), path ?: "")
}
}
}
private fun getDuplicateContacts(callback: () -> Unit) {
ContactsHelper(this).getDuplicatesOfContact(contact!!, false) { contacts ->
ensureBackgroundThread {

View File

@ -15,7 +15,10 @@ import com.bumptech.glide.signature.ObjectKey
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.getTextSize
import com.simplemobiletools.commons.extensions.highlightTextFromNumbers
import com.simplemobiletools.commons.extensions.highlightTextPart
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.views.FastScroller

View File

@ -3,11 +3,13 @@ package com.simplemobiletools.contacts.pro.adapters
import android.view.View
import android.view.ViewGroup
import androidx.viewpager.widget.PagerAdapter
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
import com.simplemobiletools.commons.helpers.TAB_CONTACTS
import com.simplemobiletools.commons.helpers.TAB_FAVORITES
import com.simplemobiletools.commons.helpers.TAB_GROUPS
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.activities.SimpleActivity
import com.simplemobiletools.contacts.pro.extensions.config
import com.simplemobiletools.contacts.pro.fragments.MyViewPagerFragment
class ViewPagerAdapter(val activity: SimpleActivity, val currTabsList: ArrayList<Int>, val showTabs: Int) : PagerAdapter() {
@ -19,6 +21,7 @@ class ViewPagerAdapter(val activity: SimpleActivity, val currTabsList: ArrayList
(view as MyViewPagerFragment).apply {
setupFragment(activity)
setupColors(activity.config.textColor, activity.getAdjustedPrimaryColor())
}
return view

View File

@ -17,7 +17,7 @@ import com.simplemobiletools.contacts.pro.models.Group
import com.simplemobiletools.contacts.pro.models.LocalContact
import java.util.concurrent.Executors
@Database(entities = [LocalContact::class, Group::class], version = 2)
@Database(entities = [LocalContact::class, Group::class], version = 3)
@TypeConverters(Converters::class)
abstract class ContactsDatabase : RoomDatabase() {
@ -40,6 +40,7 @@ abstract class ContactsDatabase : RoomDatabase() {
}
})
.addMigrations(MIGRATION_1_2)
.addMigrations(MIGRATION_2_3)
.build()
}
}
@ -77,5 +78,13 @@ abstract class ContactsDatabase : RoomDatabase() {
}
}
}
private val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.apply {
execSQL("ALTER TABLE contacts ADD COLUMN ringtone TEXT DEFAULT ''")
}
}
}
}
}

View File

@ -30,6 +30,7 @@ class ManageVisibleFieldsDialog(val activity: BaseSimpleActivity) {
put(SHOW_WEBSITES_FIELD, R.id.manage_visible_fields_websites)
put(SHOW_GROUPS_FIELD, R.id.manage_visible_fields_groups)
put(SHOW_CONTACT_SOURCE_FIELD, R.id.manage_visible_fields_contact_source)
put(SHOW_RINGTONE_FIELD, R.id.manage_ringtone)
}
val showContactFields = activity.config.showContactFields
@ -38,11 +39,11 @@ class ManageVisibleFieldsDialog(val activity: BaseSimpleActivity) {
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
.setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
}
private fun dialogConfirmed() {

View File

@ -2,6 +2,7 @@ package com.simplemobiletools.contacts.pro.dialogs
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.views.MyAppCompatCheckbox
import com.simplemobiletools.contacts.pro.R
@ -51,11 +52,12 @@ class SelectGroupsDialog(val activity: SimpleActivity, val selectedGroups: Array
item_checkbox_holder.setOnClickListener {
item_checkbox.toggle()
}
item_checkbox.apply {
isChecked = selectedGroups.contains(group)
text = group.title
tag = group.id
setColors(config.textColor, config.primaryColor, config.backgroundColor)
setColors(config.textColor, activity.getAdjustedPrimaryColor(), config.backgroundColor)
}
view.dialog_groups_holder.addView(this)
}

View File

@ -36,7 +36,7 @@ fun Context.getEmptyContact(): Contact {
val originalContactSource = if (hasContactPermissions()) config.lastUsedContactSource else SMT_PRIVATE
val organization = Organization("", "")
return Contact(0, "", "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), originalContactSource, 0, 0, "",
null, "", ArrayList(), organization, ArrayList(), ArrayList())
null, "", ArrayList(), organization, ArrayList(), ArrayList(), DEFAULT_MIMETYPE, null)
}
fun Context.viewContact(contact: Contact) {
@ -66,17 +66,6 @@ fun Context.sendEmailIntent(recipient: String) {
}
}
fun Context.sendSMSIntent(recipient: String) {
Intent(Intent.ACTION_SENDTO).apply {
data = Uri.fromParts("smsto", recipient, null)
if (resolveActivity(packageManager) != null) {
startActivity(this)
} else {
toast(R.string.no_app_found)
}
}
}
fun Context.sendAddressIntent(address: String) {
val location = Uri.encode(address)
val uri = Uri.parse("geo:0,0?q=$location")
@ -91,8 +80,14 @@ fun Context.sendAddressIntent(address: String) {
}
fun Context.openWebsiteIntent(url: String) {
val website = if (url.startsWith("http")) {
url
} else {
"https://$url"
}
Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(url)
data = Uri.parse(website)
if (resolveActivity(packageManager) != null) {
startActivity(this)
} else {
@ -364,6 +359,10 @@ fun Context.getSocialActions(id: Int): ArrayList<SocialAction> {
"vnd.android.cursor.item/vnd.org.telegram.messenger.android.call" -> SOCIAL_VOICE_CALL
"vnd.android.cursor.item/vnd.org.telegram.messenger.android.call.video" -> SOCIAL_VIDEO_CALL
"vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile" -> SOCIAL_MESSAGE
// Threema
"vnd.android.cursor.item/vnd.ch.threema.app.profile" -> SOCIAL_MESSAGE
"vnd.android.cursor.item/vnd.ch.threema.app.call" -> SOCIAL_VOICE_CALL
else -> return@queryCursor
}

View File

@ -61,7 +61,6 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
fragment_placeholder_2?.underlineText()
updateViewStuff()
when {
this is FavoritesFragment -> {
@ -76,22 +75,24 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
}
fun textColorChanged(color: Int) {
fun setupColors(textColor: Int, adjustedPrimaryColor: Int) {
when {
this is GroupsFragment -> (fragment_list.adapter as GroupsAdapter).updateTextColor(color)
this is GroupsFragment -> (fragment_list.adapter as? GroupsAdapter)?.updateTextColor(textColor)
else -> (fragment_list.adapter as? ContactsAdapter)?.apply {
updateTextColor(color)
updateTextColor(textColor)
}
}
letter_fastscroller?.textColor = color.getColorStateList()
}
fun primaryColorChanged() {
context.updateTextColors(fragment_wrapper.parent as ViewGroup)
fragment_fastscroller?.updatePrimaryColor()
fragment_fastscroller?.updateBubblePrimaryColor()
letter_fastscroller_thumb?.thumbColor = config.primaryColor.getColorStateList()
letter_fastscroller_thumb?.textColor = config.primaryColor.getContrastColor()
fragment_placeholder_2?.setTextColor(adjustedPrimaryColor)
letter_fastscroller?.textColor = textColor.getColorStateList()
letter_fastscroller?.pressedTextColor = adjustedPrimaryColor
letter_fastscroller_thumb?.fontSize = context.getTextSize()
letter_fastscroller_thumb?.textColor = adjustedPrimaryColor.getContrastColor()
letter_fastscroller_thumb?.thumbColor = adjustedPrimaryColor.getColorStateList()
}
fun startNameWithSurnameChanged(startNameWithSurname: Boolean) {
@ -143,11 +144,8 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
} else {
setupContactsFavoritesAdapter(contacts)
contactsIgnoringSearch = (fragment_list?.adapter as? ContactsAdapter)?.contactItems ?: ArrayList()
letter_fastscroller.textColor = config.textColor.getColorStateList()
setupLetterFastscroller(contacts)
letter_fastscroller_thumb.setupWithFastScroller(letter_fastscroller)
letter_fastscroller_thumb.textColor = config.primaryColor.getContrastColor()
}
}
@ -274,10 +272,6 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
}
fun onActivityResume() {
updateViewStuff()
}
fun finishActMode() {
(fragment_list.adapter as? MyRecyclerViewAdapter)?.finishActMode()
}
@ -288,18 +282,18 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
val shouldNormalize = text.normalizeString() == text
val filtered = contactsIgnoringSearch.filter {
getProperText(it.getNameToDisplay(), shouldNormalize).contains(text, true) ||
getProperText(it.nickname, shouldNormalize).contains(text, true) ||
it.phoneNumbers.any {
text.normalizePhoneNumber().isNotEmpty() && (it.normalizedNumber
?: it.value).contains(text.normalizePhoneNumber(), true)
} ||
it.emails.any { it.value.contains(text, true) } ||
it.addresses.any { getProperText(it.value, shouldNormalize).contains(text, true) } ||
it.IMs.any { it.value.contains(text, true) } ||
getProperText(it.notes, shouldNormalize).contains(text, true) ||
getProperText(it.organization.company, shouldNormalize).contains(text, true) ||
getProperText(it.organization.jobPosition, shouldNormalize).contains(text, true) ||
it.websites.any { it.contains(text, true) }
getProperText(it.nickname, shouldNormalize).contains(text, true) ||
it.phoneNumbers.any {
text.normalizePhoneNumber().isNotEmpty() && (it.normalizedNumber
?: it.value).contains(text.normalizePhoneNumber(), true)
} ||
it.emails.any { it.value.contains(text, true) } ||
it.addresses.any { getProperText(it.value, shouldNormalize).contains(text, true) } ||
it.IMs.any { it.value.contains(text, true) } ||
getProperText(it.notes, shouldNormalize).contains(text, true) ||
getProperText(it.organization.company, shouldNormalize).contains(text, true) ||
getProperText(it.organization.jobPosition, shouldNormalize).contains(text, true) ||
it.websites.any { it.contains(text, true) }
} as ArrayList
filtered.sortBy {
@ -348,13 +342,6 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
}
private fun updateViewStuff() {
context.updateTextColors(fragment_wrapper.parent as ViewGroup)
fragment_fastscroller?.updateBubbleColors()
fragment_placeholder_2?.setTextColor(context.getAdjustedPrimaryColor())
letter_fastscroller_thumb?.fontSize = context.getTextSize()
}
private fun setupViewVisibility(hasItemsToShow: Boolean) {
fragment_placeholder_2?.beVisibleIf(!hasItemsToShow)
fragment_placeholder?.beVisibleIf(!hasItemsToShow)

View File

@ -93,6 +93,7 @@ const val SHOW_CONTACT_SOURCE_FIELD = 4096
const val SHOW_WEBSITES_FIELD = 8192
const val SHOW_NICKNAME_FIELD = 16384
const val SHOW_IMS_FIELD = 32768
const val SHOW_RINGTONE_FIELD = 65536
const val DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME
const val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE
@ -101,22 +102,25 @@ const val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY
const val DEFAULT_ORGANIZATION_TYPE = CommonDataKinds.Organization.TYPE_WORK
const val DEFAULT_WEBSITE_TYPE = CommonDataKinds.Website.TYPE_HOMEPAGE
const val DEFAULT_IM_TYPE = CommonDataKinds.Im.PROTOCOL_SKYPE
const val DEFAULT_MIMETYPE = CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
// apps with special handling
const val TELEGRAM_PACKAGE = "org.telegram.messenger"
const val SIGNAL_PACKAGE = "org.thoughtcrime.securesms"
const val WHATSAPP_PACKAGE = "com.whatsapp"
const val VIBER_PACKAGE = "com.viber.voip"
const val THREEMA_PACKAGE = "ch.threema.app"
const val WHATSAPP = "whatsapp"
const val SIGNAL = "signal"
const val VIBER = "viber"
const val TELEGRAM = "telegram"
const val THREEMA = "threema"
const val SOCIAL_VOICE_CALL = 0
const val SOCIAL_VIDEO_CALL = 1
const val SOCIAL_MESSAGE = 2
fun getEmptyLocalContact() = LocalContact(0, "", "", "", "", "", "", null, "", ArrayList(), ArrayList(), ArrayList(), 0, ArrayList(), "", ArrayList(), "", "", ArrayList(), ArrayList())
fun getEmptyLocalContact() = LocalContact(0, "", "", "", "", "", "", null, "", ArrayList(), ArrayList(), ArrayList(), 0, ArrayList(), "", ArrayList(), "", "", ArrayList(), ArrayList(), null)
fun getProperText(text: String, shouldNormalize: Boolean) = if (shouldNormalize) text.normalizeString() else text

View File

@ -28,7 +28,7 @@ class ContactsHelper(val context: Context) {
private val BATCH_SIZE = 50
private var displayContactSources = ArrayList<String>()
fun getContacts(getAll: Boolean = false, ignoredContactSources: HashSet<String> = HashSet(), callback: (ArrayList<Contact>) -> Unit) {
fun getContacts(getAll: Boolean = false, gettingDuplicates: Boolean = false, ignoredContactSources: HashSet<String> = HashSet(), callback: (ArrayList<Contact>) -> Unit) {
ensureBackgroundThread {
val contacts = SparseArray<Contact>()
displayContactSources = context.getVisibleContactSources()
@ -43,7 +43,7 @@ class ContactsHelper(val context: Context) {
}
}
getDeviceContacts(contacts, ignoredContactSources)
getDeviceContacts(contacts, ignoredContactSources, gettingDuplicates)
if (displayContactSources.contains(SMT_PRIVATE)) {
LocalContactsHelper(context).getAllContacts().forEach {
@ -125,7 +125,7 @@ class ContactsHelper(val context: Context) {
}
}
private fun getDeviceContacts(contacts: SparseArray<Contact>, ignoredContactSources: HashSet<String>?) {
private fun getDeviceContacts(contacts: SparseArray<Contact>, ignoredContactSources: HashSet<String>?, gettingDuplicates: Boolean) {
if (!context.hasContactPermissions()) {
return
}
@ -134,55 +134,87 @@ class ContactsHelper(val context: Context) {
val uri = Data.CONTENT_URI
val projection = getContactProjection()
val selection = "${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?"
val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
val sortOrder = getSortString()
arrayOf(CommonDataKinds.Organization.CONTENT_ITEM_TYPE, StructuredName.CONTENT_ITEM_TYPE).forEach { mimetype ->
val selection = "${Data.MIMETYPE} = ?"
val selectionArgs = arrayOf(mimetype)
val sortOrder = getSortString()
context.queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor ->
val accountName = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: ""
val accountType = cursor.getStringValue(RawContacts.ACCOUNT_TYPE) ?: ""
if (ignoredSources.contains("$accountName:$accountType")) {
return@queryCursor
context.queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor ->
val accountName = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: ""
val accountType = cursor.getStringValue(RawContacts.ACCOUNT_TYPE) ?: ""
if (ignoredSources.contains("$accountName:$accountType")) {
return@queryCursor
}
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
var prefix = ""
var firstName = ""
var middleName = ""
var surname = ""
var suffix = ""
// ignore names at Organization type contacts
if (mimetype == StructuredName.CONTENT_ITEM_TYPE) {
prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: ""
suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: ""
}
var photoUri = ""
var starred = 0
var contactId = 0
var thumbnailUri = ""
var ringtone: String? = null
if (!gettingDuplicates) {
photoUri = cursor.getStringValue(StructuredName.PHOTO_URI) ?: ""
starred = cursor.getIntValue(StructuredName.STARRED)
contactId = cursor.getIntValue(Data.CONTACT_ID)
thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
ringtone = cursor.getStringValue(StructuredName.CUSTOM_RINGTONE)
}
val nickname = ""
val numbers = ArrayList<PhoneNumber>() // proper value is obtained below
val emails = ArrayList<Email>()
val addresses = ArrayList<Address>()
val events = ArrayList<Event>()
val notes = ""
val groups = ArrayList<Group>()
val organization = Organization("", "")
val websites = ArrayList<String>()
val ims = ArrayList<IM>()
val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, numbers, emails, addresses,
events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims, mimetype, ringtone)
contacts.put(id, contact)
}
}
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
var prefix = ""
var firstName = ""
var middleName = ""
var surname = ""
var suffix = ""
val emails = getEmails()
var size = emails.size()
for (i in 0 until size) {
val key = emails.keyAt(i)
contacts[key]?.emails = emails.valueAt(i)
}
// ignore names at Organization type contacts
if (cursor.getStringValue(Data.MIMETYPE) == StructuredName.CONTENT_ITEM_TYPE) {
prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: ""
suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: ""
}
val organizations = getOrganizations()
size = organizations.size()
for (i in 0 until size) {
val key = organizations.keyAt(i)
contacts[key]?.organization = organizations.valueAt(i)
}
val nickname = ""
val photoUri = cursor.getStringValue(StructuredName.PHOTO_URI) ?: ""
val numbers = ArrayList<PhoneNumber>() // proper value is obtained below
val emails = ArrayList<Email>()
val addresses = ArrayList<Address>()
val events = ArrayList<Event>()
val starred = cursor.getIntValue(StructuredName.STARRED)
val contactId = cursor.getIntValue(Data.CONTACT_ID)
val thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
val notes = ""
val groups = ArrayList<Group>()
val organization = Organization("", "")
val websites = ArrayList<String>()
val ims = ArrayList<IM>()
val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, numbers, emails, addresses,
events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims)
contacts.put(id, contact)
// no need to fetch some fields if we are only getting duplicates of the current contact
if (gettingDuplicates) {
return
}
val phoneNumbers = getPhoneNumbers(null)
var size = phoneNumbers.size()
size = phoneNumbers.size()
for (i in 0 until size) {
val key = phoneNumbers.keyAt(i)
if (contacts[key] != null) {
@ -191,20 +223,6 @@ class ContactsHelper(val context: Context) {
}
}
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()
size = emails.size()
for (i in 0 until size) {
val key = emails.keyAt(i)
contacts[key]?.emails = emails.valueAt(i)
}
val addresses = getAddresses()
size = addresses.size()
for (i in 0 until size) {
@ -233,13 +251,6 @@ class ContactsHelper(val context: Context) {
contacts[key]?.notes = notes.valueAt(i)
}
val organizations = getOrganizations()
size = organizations.size()
for (i in 0 until size) {
val key = organizations.keyAt(i)
contacts[key]?.organization = organizations.valueAt(i)
}
val websites = getWebsites()
size = websites.size()
for (i in 0 until size) {
@ -695,9 +706,10 @@ class ContactsHelper(val context: Context) {
var middleName = ""
var surname = ""
var suffix = ""
val mimetype = cursor.getStringValue(Data.MIMETYPE)
// ignore names at Organization type contacts
if (cursor.getStringValue(Data.MIMETYPE) == StructuredName.CONTENT_ITEM_TYPE) {
if (mimetype == StructuredName.CONTENT_ITEM_TYPE) {
prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
@ -714,6 +726,7 @@ class ContactsHelper(val context: Context) {
val notes = getNotes(id)[id] ?: ""
val accountName = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: ""
val starred = cursor.getIntValue(StructuredName.STARRED)
val ringtone = cursor.getStringValue(StructuredName.CUSTOM_RINGTONE)
val contactId = cursor.getIntValue(Data.CONTACT_ID)
val groups = getContactGroups(storedGroups, contactId)[contactId] ?: ArrayList()
val thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
@ -721,7 +734,7 @@ class ContactsHelper(val context: Context) {
val websites = getWebsites(id)[id] ?: ArrayList()
val ims = getIMs(id)[id] ?: ArrayList()
return Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses, events,
accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims)
accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims, mimetype, ringtone)
}
}
@ -745,7 +758,8 @@ class ContactsHelper(val context: Context) {
val ignoredTypes = arrayListOf(
SIGNAL_PACKAGE,
TELEGRAM_PACKAGE,
WHATSAPP_PACKAGE
WHATSAPP_PACKAGE,
THREEMA_PACKAGE
)
val contactSources = getContactSourcesSync()
@ -819,6 +833,7 @@ class ContactsHelper(val context: Context) {
StructuredName.PHOTO_URI,
StructuredName.PHOTO_THUMBNAIL_URI,
StructuredName.STARRED,
StructuredName.CUSTOM_RINGTONE,
RawContacts.ACCOUNT_NAME,
RawContacts.ACCOUNT_TYPE
)
@ -858,8 +873,8 @@ class ContactsHelper(val context: Context) {
try {
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newUpdate(Data.CONTENT_URI).apply {
val selection = "${Data.RAW_CONTACT_ID} = ? AND (${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?)"
val selectionArgs = arrayOf(contact.id.toString(), StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ?"
val selectionArgs = arrayOf(contact.id.toString(), contact.mimetype)
withSelection(selection, selectionArgs)
withValue(StructuredName.PREFIX, contact.prefix)
withValue(StructuredName.GIVEN_NAME, contact.firstName)
@ -1010,14 +1025,16 @@ class ContactsHelper(val context: Context) {
}
// add organization
ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
withValue(Data.RAW_CONTACT_ID, contact.id)
withValue(Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Organization.COMPANY, contact.organization.company)
withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE)
withValue(CommonDataKinds.Organization.TITLE, contact.organization.jobPosition)
withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE)
operations.add(build())
if (contact.organization.isNotEmpty()) {
ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
withValue(Data.RAW_CONTACT_ID, contact.id)
withValue(Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Organization.COMPANY, contact.organization.company)
withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE)
withValue(CommonDataKinds.Organization.TITLE, contact.organization.jobPosition)
withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE)
operations.add(build())
}
}
// delete websites
@ -1061,11 +1078,12 @@ class ContactsHelper(val context: Context) {
}
}
// favorite
// favorite, ringtone
try {
val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, contact.contactId.toString())
val contentValues = ContentValues(1)
val contentValues = ContentValues(2)
contentValues.put(Contacts.STARRED, contact.starred)
contentValues.put(Contacts.CUSTOM_RINGTONE, contact.ringtone)
context.contentResolver.update(uri, contentValues, null, null)
} catch (e: Exception) {
context.showErrorToast(e)
@ -1327,12 +1345,13 @@ class ContactsHelper(val context: Context) {
addFullSizePhoto(rawId, fullSizePhotoData)
}
// favorite
// favorite, ringtone
val userId = getRealContactId(rawId)
if (userId != 0 && contact.starred == 1) {
if (userId != 0) {
val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, userId.toString())
val contentValues = ContentValues(1)
val contentValues = ContentValues(2)
contentValues.put(Contacts.STARRED, contact.starred)
contentValues.put(Contacts.CUSTOM_RINGTONE, contact.ringtone)
context.contentResolver.update(uri, contentValues, null, null)
}
@ -1413,6 +1432,21 @@ class ContactsHelper(val context: Context) {
LocalContactsHelper(context).toggleFavorites(localContacts, addToFavorites)
}
fun updateRingtone(contactId: String, newUri: String) {
try {
val operations = ArrayList<ContentProviderOperation>()
val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, contactId)
ContentProviderOperation.newUpdate(uri).apply {
withValue(Contacts.CUSTOM_RINGTONE, newUri)
operations.add(build())
}
context.contentResolver.applyBatch(AUTHORITY, operations)
} catch (e: Exception) {
context.showErrorToast(e)
}
}
fun deleteContact(originalContact: Contact, deleteClones: Boolean = false, callback: (success: Boolean) -> Unit) {
ensureBackgroundThread {
if (deleteClones) {
@ -1463,7 +1497,7 @@ class ContactsHelper(val context: Context) {
fun getDuplicatesOfContact(contact: Contact, addOriginal: Boolean, callback: (ArrayList<Contact>) -> Unit) {
ensureBackgroundThread {
getContacts(true) { contacts ->
getContacts(true, true) { contacts ->
val duplicates = contacts.filter { it.id != contact.id && it.getHashToCompare() == contact.getHashToCompare() }.toMutableList() as ArrayList<Contact>
if (addOriginal) {
duplicates.add(contact)

View File

@ -6,7 +6,6 @@ import android.graphics.BitmapFactory
import android.net.Uri
import android.provider.ContactsContract.CommonDataKinds.Event
import android.provider.MediaStore
import com.simplemobiletools.commons.extensions.getChoppedList
import com.simplemobiletools.commons.models.SimpleContact
import com.simplemobiletools.contacts.pro.extensions.contactsDB
import com.simplemobiletools.contacts.pro.extensions.getByteArray
@ -56,7 +55,7 @@ class LocalContactsHelper(val context: Context) {
}
fun deleteContactIds(ids: MutableList<Long>) {
ids.getChoppedList().forEach {
ids.chunked(30).forEach {
context.contactsDB.deleteContactIds(it)
}
}
@ -68,6 +67,10 @@ class LocalContactsHelper(val context: Context) {
}
}
fun updateRingtone(id: Int, ringtone: String) {
context.contactsDB.updateRingtone(ringtone, id)
}
private fun getPhotoByteArray(uri: String): ByteArray {
if (uri.isEmpty()) {
return ByteArray(0)
@ -83,7 +86,7 @@ class LocalContactsHelper(val context: Context) {
return scaledSizePhotoData
}
fun convertLocalContactToContact(localContact: LocalContact?, storedGroups: ArrayList<Group>): Contact? {
private fun convertLocalContactToContact(localContact: LocalContact?, storedGroups: ArrayList<Group>): Contact? {
if (localContact == null) {
return null
}
@ -121,6 +124,7 @@ class LocalContactsHelper(val context: Context) {
organization = Organization(localContact.company, localContact.jobPosition)
websites = localContact.websites
IMs = localContact.IMs
ringtone = localContact.ringtone
}
}
@ -151,6 +155,7 @@ class LocalContactsHelper(val context: Context) {
jobPosition = contact.organization.jobPosition
websites = contact.websites
IMs = contact.IMs
ringtone = contact.ringtone
}
}

View File

@ -115,6 +115,7 @@ class VcfImporter(val activity: SimpleActivity) {
val photoData = ezContact.photos.firstOrNull()?.data
val photo = null
val thumbnailUri = savePhoto(photoData)
val ringtone = null
val IMs = ArrayList<IM>()
ezContact.impps.forEach {
@ -138,11 +139,16 @@ class VcfImporter(val activity: SimpleActivity) {
}
val contact = Contact(0, prefix, firstName, middleName, surname, suffix, nickname, photoUri, phoneNumbers, emails, addresses, events,
targetContactSource, starred, contactId, thumbnailUri, photo, notes, groups, organization, websites, IMs)
targetContactSource, starred, contactId, thumbnailUri, photo, notes, groups, organization, websites, IMs, DEFAULT_MIMETYPE, ringtone)
// if there is no N and ORG fields at the given contact, only FN, treat it as an organization
if (contact.getNameToDisplay().isEmpty() && contact.organization.isEmpty() && ezContact.formattedName?.value?.isNotEmpty() == true) {
contact.organization.company = ezContact.formattedName.value
contact.mimetype = CommonDataKinds.Organization.CONTENT_ITEM_TYPE
}
if (contact.isABusinessContact()) {
contact.mimetype = CommonDataKinds.Organization.CONTENT_ITEM_TYPE
}
if (ContactsHelper(activity).insertContact(contact)) {

View File

@ -23,6 +23,9 @@ interface ContactsDao {
@Query("UPDATE contacts SET starred = :isStarred WHERE id = :id")
fun updateStarred(isStarred: Int, id: Int)
@Query("UPDATE contacts SET ringtone = :ringtone WHERE id = :id")
fun updateRingtone(ringtone: String, id: Int)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrUpdate(contact: LocalContact): Long

View File

@ -13,8 +13,9 @@ import com.simplemobiletools.contacts.pro.helpers.SMT_PRIVATE
data class Contact(var id: Int, var prefix: String, var firstName: String, var middleName: String, var surname: String, var suffix: String, var nickname: String,
var photoUri: String, var phoneNumbers: ArrayList<PhoneNumber>, var emails: ArrayList<Email>, var addresses: ArrayList<Address>,
var events: ArrayList<Event>, var source: String, var starred: Int, var contactId: Int, var thumbnailUri: String, var photo: Bitmap?, var notes: String,
var groups: ArrayList<Group>, var organization: Organization, var websites: ArrayList<String>, var IMs: ArrayList<IM>) :
Comparable<Contact> {
var groups: ArrayList<Group>, var organization: Organization, var websites: ArrayList<String>, var IMs: ArrayList<IM>, var mimetype: String,
var ringtone: String?) :
Comparable<Contact> {
companion object {
var sorting = 0
var startWithSurname = false
@ -71,7 +72,7 @@ data class Contact(var id: Int, var prefix: String, var firstName: String, var m
} else if (firstString.isNotEmpty() && secondString.isEmpty()) {
-1
} else {
if (firstString.toLowerCase() == secondString.toLowerCase()) {
if (firstString.equals(secondString, ignoreCase = true)) {
getNameToDisplay().compareTo(other.getNameToDisplay(), true)
} else {
firstString.compareTo(secondString, true)
@ -112,7 +113,7 @@ data class Contact(var id: Int, var prefix: String, var firstName: String, var m
fun getStringToCompare(): String {
return copy(id = 0, prefix = "", firstName = getNameToDisplay().toLowerCase(), middleName = "", surname = "", suffix = "", nickname = "", photoUri = "",
phoneNumbers = ArrayList(), emails = ArrayList(), events = ArrayList(), source = "", addresses = ArrayList(), starred = 0, contactId = 0,
thumbnailUri = "", notes = "", groups = ArrayList(), websites = ArrayList(), organization = Organization("", ""), IMs = ArrayList()).toString()
thumbnailUri = "", notes = "", groups = ArrayList(), websites = ArrayList(), organization = Organization("", ""), IMs = ArrayList(), ringtone = "").toString()
}
fun getHashToCompare() = getStringToCompare().hashCode()
@ -130,9 +131,9 @@ data class Contact(var id: Int, var prefix: String, var firstName: String, var m
val normalizedText = if (convertLetters) text.normalizePhoneNumber() else text
phoneNumbers.any {
PhoneNumberUtils.compare(it.normalizedNumber, normalizedText) ||
it.value.contains(text) ||
it.normalizedNumber?.contains(normalizedText) == true ||
it.value.normalizePhoneNumber().contains(normalizedText)
it.value.contains(text) ||
it.normalizedNumber?.contains(normalizedText) == true ||
it.value.normalizePhoneNumber().contains(normalizedText)
}
} else {
false

View File

@ -7,26 +7,27 @@ import androidx.room.PrimaryKey
@Entity(tableName = "contacts", indices = [(Index(value = ["id"], unique = true))])
data class LocalContact(
@PrimaryKey(autoGenerate = true) var id: Int?,
@ColumnInfo(name = "prefix") var prefix: String,
@ColumnInfo(name = "first_name") var firstName: String,
@ColumnInfo(name = "middle_name") var middleName: String,
@ColumnInfo(name = "surname") var surname: String,
@ColumnInfo(name = "suffix") var suffix: String,
@ColumnInfo(name = "nickname") var nickname: String,
@ColumnInfo(name = "photo", typeAffinity = ColumnInfo.BLOB) var photo: ByteArray?,
@ColumnInfo(name = "photo_uri") var photoUri: String,
@ColumnInfo(name = "phone_numbers") var phoneNumbers: ArrayList<PhoneNumber>,
@ColumnInfo(name = "emails") var emails: ArrayList<Email>,
@ColumnInfo(name = "events") var events: ArrayList<Event>,
@ColumnInfo(name = "starred") var starred: Int,
@ColumnInfo(name = "addresses") var addresses: ArrayList<Address>,
@ColumnInfo(name = "notes") var notes: String,
@ColumnInfo(name = "groups") var groups: ArrayList<Long>,
@ColumnInfo(name = "company") var company: String,
@ColumnInfo(name = "job_position") var jobPosition: String,
@ColumnInfo(name = "websites") var websites: ArrayList<String>,
@ColumnInfo(name = "ims") var IMs: ArrayList<IM>) {
@PrimaryKey(autoGenerate = true) var id: Int?,
@ColumnInfo(name = "prefix") var prefix: String,
@ColumnInfo(name = "first_name") var firstName: String,
@ColumnInfo(name = "middle_name") var middleName: String,
@ColumnInfo(name = "surname") var surname: String,
@ColumnInfo(name = "suffix") var suffix: String,
@ColumnInfo(name = "nickname") var nickname: String,
@ColumnInfo(name = "photo", typeAffinity = ColumnInfo.BLOB) var photo: ByteArray?,
@ColumnInfo(name = "photo_uri") var photoUri: String,
@ColumnInfo(name = "phone_numbers") var phoneNumbers: ArrayList<PhoneNumber>,
@ColumnInfo(name = "emails") var emails: ArrayList<Email>,
@ColumnInfo(name = "events") var events: ArrayList<Event>,
@ColumnInfo(name = "starred") var starred: Int,
@ColumnInfo(name = "addresses") var addresses: ArrayList<Address>,
@ColumnInfo(name = "notes") var notes: String,
@ColumnInfo(name = "groups") var groups: ArrayList<Long>,
@ColumnInfo(name = "company") var company: String,
@ColumnInfo(name = "job_position") var jobPosition: String,
@ColumnInfo(name = "websites") var websites: ArrayList<String>,
@ColumnInfo(name = "ims") var IMs: ArrayList<IM>,
@ColumnInfo(name = "ringtone") var ringtone: String?) {
override fun equals(other: Any?) = id == (other as? LocalContact?)?.id

View File

@ -384,6 +384,31 @@
android:textCursorDrawable="@null"
android:textSize="@dimen/bigger_text_size" />
<ImageView
android:id="@+id/contact_ringtone_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_ringtone"
android:layout_marginStart="@dimen/normal_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingBottom="@dimen/medium_margin"
android:src="@drawable/ic_bell_vector" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_ringtone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_notes"
android:layout_centerVertical="true"
android:layout_marginTop="@dimen/normal_margin"
android:layout_marginEnd="@dimen/activity_margin"
android:layout_toEndOf="@+id/contact_notes_image"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="@dimen/contact_icons_size"
android:textSize="@dimen/bigger_text_size" />
<ImageView
android:id="@+id/contact_organization_image"
android:layout_width="@dimen/contact_icons_size"
@ -399,7 +424,7 @@
android:id="@+id/contact_organization_company"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_notes"
android:layout_below="@+id/contact_ringtone"
android:layout_centerVertical="true"
android:layout_marginTop="@dimen/normal_margin"
android:layout_marginEnd="@dimen/activity_margin"

View File

@ -161,6 +161,39 @@
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_on_contact_click_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingStart="@dimen/normal_margin"
android:paddingTop="@dimen/bigger_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/bigger_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_on_contact_click_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/settings_on_contact_click"
android:paddingStart="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin"
android:text="@string/on_contact_click" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_on_contact_click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="@dimen/small_margin"
android:background="@null"
android:clickable="false" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_show_contact_thumbnails_holder"
android:layout_width="match_parent"
@ -321,37 +354,5 @@
app:switchPadding="@dimen/medium_margin" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_on_contact_click_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingStart="@dimen/normal_margin"
android:paddingTop="@dimen/bigger_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/bigger_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_on_contact_click_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/settings_on_contact_click"
android:paddingStart="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin"
android:text="@string/on_contact_click" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_on_contact_click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="@dimen/small_margin"
android:background="@null"
android:clickable="false" />
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -104,7 +104,7 @@
android:src="@drawable/ic_person_vector" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_prefix"
android:id="@+id/contact_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_photo"
@ -118,92 +118,8 @@
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_prefix"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/contact_name_image"
android:background="?attr/selectableItemBackground"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_middle_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_first_name"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/contact_name_image"
android:background="?attr/selectableItemBackground"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_surname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_middle_name"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/contact_name_image"
android:background="?attr/selectableItemBackground"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_suffix"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_surname"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/contact_name_image"
android:background="?attr/selectableItemBackground"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:singleLine="true"
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_toEndOf="@+id/contact_name_image"
android:background="?attr/selectableItemBackground"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size" />
android:textSize="@dimen/bigger_text_size"
android:tooltipText="John Doe" />
<ImageView
android:id="@+id/contact_numbers_image"
@ -277,6 +193,18 @@
android:src="@drawable/ic_label_vector"
android:visibility="gone" />
<ImageView
android:id="@+id/contact_ringtone_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_ringtone"
android:layout_marginStart="@dimen/normal_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingBottom="@dimen/small_margin"
android:src="@drawable/ic_bell_vector"
android:visibility="gone" />
<ImageView
android:id="@+id/contact_organization_image"
android:layout_width="@dimen/contact_icons_size"
@ -329,7 +257,7 @@
android:id="@+id/contact_numbers_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_nickname"
android:layout_below="@+id/contact_name"
android:layout_toEndOf="@+id/contact_numbers_image"
android:orientation="vertical"
android:visibility="gone" />
@ -413,11 +341,26 @@
android:visibility="gone" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_organization_company"
android:id="@+id/contact_ringtone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_notes"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/contact_ringtone_image"
android:background="?attr/selectableItemBackground"
android:lineSpacingExtra="@dimen/medium_margin"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:textSize="@dimen/bigger_text_size"
android:visibility="gone" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_organization_company"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_ringtone"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/contact_organization_image"
android:background="?attr/selectableItemBackground"
android:lineSpacingExtra="@dimen/medium_margin"

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sorting_dialog_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -11,8 +10,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin">
android:paddingTop="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin">
<RadioGroup
android:id="@+id/sorting_dialog_radio_sorting"
@ -24,38 +23,37 @@
android:id="@+id/sorting_dialog_radio_first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/first_name"/>
android:paddingBottom="@dimen/medium_margin"
android:text="@string/first_name" />
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/sorting_dialog_radio_middle_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/middle_name"/>
android:paddingBottom="@dimen/medium_margin"
android:text="@string/middle_name" />
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/sorting_dialog_radio_surname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/surname"/>
android:paddingBottom="@dimen/medium_margin"
android:text="@string/surname" />
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/sorting_dialog_radio_full_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/full_name"/>
android:paddingBottom="@dimen/medium_margin"
android:text="@string/full_name" />
</RadioGroup>
<include
layout="@layout/divider"/>
<include layout="@layout/divider" />
<RadioGroup
android:id="@+id/sorting_dialog_radio_order"
@ -68,17 +66,17 @@
android:id="@+id/sorting_dialog_radio_ascending"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/ascending"/>
android:paddingBottom="@dimen/medium_margin"
android:text="@string/ascending" />
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/sorting_dialog_radio_descending"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/descending"/>
android:paddingBottom="@dimen/medium_margin"
android:text="@string/descending" />
</RadioGroup>
</LinearLayout>
</ScrollView>

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/manage_visible_fields_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@ -11,136 +10,144 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin">
android:paddingTop="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_prefix"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/prefix"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/prefix" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/first_name"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/first_name" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_middle_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/middle_name"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/middle_name" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_surname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/surname"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/surname" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_suffix"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/suffix"/>
android:paddingBottom="@dimen/activity_margin"
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"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/nickname" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_phone_numbers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/phone_numbers"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/phone_numbers" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_emails"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/emails"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/emails" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_addresses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/addresses"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/addresses" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_ims"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/instant_messaging"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/instant_messaging" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_events"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/events"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/events" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_notes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/notes"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/notes" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_ringtone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/ringtone" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_organization"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/organization"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/organization" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_websites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/websites"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/websites" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_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"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/groups" />
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_contact_source"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/contact_source"/>
android:paddingBottom="@dimen/activity_margin"
android:text="@string/contact_source" />
</LinearLayout>
</ScrollView>

View File

@ -32,6 +32,7 @@
android:layout_marginEnd="@dimen/normal_margin"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="@dimen/small_margin"
android:visibility="gone"
tools:src="@drawable/ic_phone_vector" />
</RelativeLayout>

View File

@ -105,6 +105,7 @@
<string name="groups">مجموعات</string>
<string name="contact_source">مصدر الاتصال</string>
<string name="instant_messaging">المراسلة الفورية (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Qruplar </string>
<string name="contact_source">Kontakt kökü</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Skupiny</string>
<string name="contact_source">Zdroje kontaktů</string>
<string name="instant_messaging">Rychlé zprávy (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Kontakt bude vymazán ze všech zdrojů kontaktů.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grwpiau</string>
<string name="contact_source">Ffynhonnell gyswllt</string>
<string name="instant_messaging">Negesu ar unwaith (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Groupper</string>
<string name="contact_source">Kontaktkilde</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Gruppen</string>
<string name="contact_source">Kontaktquelle</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Der Kontakt wird von allen Kontaktquellen entfernt.</string>
@ -124,38 +125,38 @@
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Verwalten Sie Ihre Kontakte professionell, schnell und sicher mit dieser App</string>
<string name="app_long_description">
A lightweight app for managing your contacts loved by millions of people. The contacts can be stored on your device only, but also synchronized via Google, or other accounts.
Eine simple App um deine Kontakte zu verwalten, die von Millionen Menschen geliebt wird. Die Kontakte können entweder lokal auf dem Gerät gespeichert oder via Google und anderen Accounts synchronisiert werden.
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.
Sie können die App auch nutzen, um Emails und Events der Kontakte zu speichern. Die App hat die Fähigkeit, mit verschiedenen Parametern zu suchen und zu sortieren und kann wahlweise den Vornamen an erster Stelle anzeigen.
You can display your favorite contacts or groups on a separate list. Groups can be used for sending out batch emails or SMS, to save you some time, you can rename them easily.
Ihre Lieblingskontakte oder -gruppen können in einer seperaten Liste angezeigt werden. Gruppen können dafür genutzt werden, um einen Stapel an Emails oder SMS zu versenden, was Ihnen Zeit erspart. Zudem können Sie diese einfach umbennen.
It contains handy buttons for calling, or texting your contacts. All visible fields can be customized as you wish, you can easily hide the unused ones. The search function will search the given string at every visible contact field, to make you find your desired contact easily.
Es sind nützliche Buttons verfügbar, um Ihre Kontakte anzurufen oder ihnen zu schreiben. Alle angezeigten Felder können nach Ihren Wünschen angepasst werden, alle ungenutzen Felder können ausgeblendet werden. Die Suchfunktion wird den angegebenen String in jedem sichtbaren Kontaktfeld suchen, sodass Sie Ihren gewüschten Kontakt einfach finden können.
There is a lightweight dialpad at your service too, with smart contact suggestions.
Es gibt ein einfaches Wählfeld, mit intelligenten Kontaktvervollständigungen.
It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
Kontakte können im vCard-Format als .vcf Dateien exportiert und importiert werden, um einfach zu migrieren oder ein Backup zu erstellen.
With this modern and stable contacts manager you can protect your contacts by not sharing them with other apps, so you can keep your contacts private.
Mit diesem modernen und stabilen Kontakte Manager können Sie Ihre Kontakte schützen, indem diese nicht mit andern Apps geteilt werden, sodass Ihre Kontakte privat bleiben.
Like the contact source, you can also easily change the contact name, email, phone number, address, organization, groups and many other customizable fields. You can use it for storing contact events too, like birthdays, anniversaries, or any other custom ones.
Genauso wie die Kontaktquelle, können Sie ebnso einfach den Kontaktnamen, dessen Email, Telefonnummer, Addresse, Organisation, Gruppe und viele andere Felder anpassen. Sie können auch Events wie Geburtstage, Feiern oder benutzerdefinierte Events angeben.
This simple contact editor has many handy settings like showing phone numbers on the main screen, toggle contact thumbnail visibility, showing only contacts with phone numbers, showing a call confirmation dialog before initiating a call. It comes with a quick dialer that also makes use of letters.
Dieser einfache Kontakte-Editor hat viele nützliche Einstellungen - zum Beispiel kann es die Telefonnummer im Hauptbildschirm anzeigen, das Kontake-Vorschaubild ein- und ausblenden, nur Kontakte mit Telefonnummern anzeigen und eine Anrufsbestätigung anzeigen, bevor der Anruf gestartet wird. Das schnelle Wahlfeld unterstützt auch Buchstaben.
To further improve the user experience, you can customize what happens at clicking on a contact. You can either initiate a call, go to the View Details screen, or edit the selected contact.
Um die Nutzererfahrung noch mehr zu verbessern, können Sie anpassen, was passiert, wenn Sie auf den Kontakt drücken. Entweder kann ein Anruf gestartet, der Kontakt angepasst (editiert) oder Details angeigt werden.
You can easily block phone numbers to avoid unwanted incoming calls.
Um ungewollte Anrufe zu vermeiden, können Anrufe einfach blockiert werden.
Um zu verhindern, dass potentiell ungewollte Kontakte angezeigt werden, führt die App Duplikate automatisch zusammen.
To avoid showing potentially unwanted contacts, it has a powerful built in duplicate contact merger.
It comes with material design and dark theme by default, provides great user experience for easy usage. The lack of internet access gives you more privacy, security and stability than other apps.
Die App beinhaltet standardmäßig material design und Dunkelmodus, um großartige Nutzererfahrungen und einfache Nutzung zu ermöglichen. Da die App keine Internetverbindung besitzt, bietet die App Ihnen mehr Datenschutz, Sicherheit und Stabilität als andere Apps.
Enthält keine Werbung oder unnötige Berechtigungen. Vollständig quelloffen. Die Farben sind anpassbar.
<b>Check out the full suite of Simple Tools here:</b>
<b>Entdecke die gesamte Serie an schlichten Apps hier:</b>
https://www.simplemobiletools.com
<b>Standalone website of Simple Contacts Pro:</b>
<b>Standalone Webseite von Simple Contacts Pro:</b>
https://www.simplemobiletools.com/contacts
<b>Facebook:</b>

View File

@ -87,11 +87,11 @@
<string name="export_contacts_to_vcf">Εξαγωγή επαφών σε .vcf αρχείο</string>
<string name="target_contact_source">Προορισμός πηγής επαφής</string>
<string name="include_contact_sources">Συμπερίληψη πηγών επαφών</string>
<string name="filename_without_vcf">Όνομα αρχείου (χωρίς .vcf)</string>
<string name="filename_without_vcf">Όνομα αρχείου (εκτός .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Πληκτρολόγιο</string>
<string name="add_number_to_contact">Προσθήκη επαφής σε αριθμό</string>
<string name="add_number_to_contact">Προσθήκη αριθμού σε επαφή</string>
<!-- Visible fields -->
<string name="prefix">Πρόθεμα</string>
@ -105,6 +105,7 @@
<string name="groups">Ομάδες</string>
<string name="contact_source">Προέλευση επαφής</string>
<string name="instant_messaging">Αμεσο μήνυμα (IM)</string>
<string name="ringtone">Ήχος κλήσης</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Η επαφή θα καταργηθεί από όλες τις πηγές επαφών.</string>

View File

@ -53,7 +53,7 @@
<string name="contacts">Contactos</string>
<string name="show_call_confirmation_dialog">Mostrar un cuadro de confirmación antes de iniciar una llamada</string>
<string name="show_only_contacts_with_numbers">Solo mostrar contactos con números telefónicos</string>
<string name="show_private_contacts">Show private contacts to Simple Dialer, Simple SMS Messenger and Simple Calendar Pro</string>
<string name="show_private_contacts">Mostrar contactos privados a Marcador Simple, Mensajería SMS Simple y Calendario Simple Pro</string>
<!-- Emails -->
<string name="home">Casa</string>
@ -105,6 +105,7 @@
<string name="groups">Grupos</string>
<string name="contact_source">Orígenes de contacto</string>
<string name="instant_messaging">Mensaje instantáneo (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">El contacto será eliminado de todos los orígenes de contactos.</string>
@ -115,7 +116,7 @@
</plurals>
<!-- FAQ -->
<string name="faq_1_title">Quiero cambiar qué acmpos son visibles en loos contactos. ¿Puedo hacerlo?</string>
<string name="faq_1_title">Quiero cambiar qué campos son visibles en los contactos. ¿Puedo hacerlo?</string>
<string name="faq_1_text">Sí, todo lo que tienes que hacer es ir a Ajustes -> Administrar campos del contacto mostrados. Ahí puedes seleccionar que campos deberían ser visibles. Algunos de ellos están desactivados por defecto, así que podrías encontrar algunos nuevos ahí.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
@ -124,35 +125,35 @@
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">La mejor app de gestión de contactos con soporte de la función de grupos</string>
<string name="app_long_description">
A lightweight app for managing your contacts loved by millions of people. The contacts can be stored on your device only, but also synchronized via Google, or other accounts.
Una ligera aplicación para administrar tus contactos amada por millones. Los contactos pueden ser almacenados únicamente en tu dispositivo, per otambién sincronizarlos vía Google u otras cuentas.
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.
Puedes usarla para administrar correos electrónicos de usuarios y eventos también. Tiene la habilidad de ordenar/filtrar por múltiples parámetros y, opcionalmente, mostrar el apellido como el nombre.
You can display your favorite contacts or groups on a separate list. Groups can be used for sending out batch emails or SMS, to save you some time, you can rename them easily.
Puedes mostrar tus contáctos favoritos o grupos en listas separadas. Los grupos pueden ser usados para enviar correos electrónicos o SMS por lotes, para ahorrate algo de tiempo, y puedes renombrarlos fácilmente.
It contains handy buttons for calling, or texting your contacts. All visible fields can be customized as you wish, you can easily hide the unused ones. The search function will search the given string at every visible contact field, to make you find your desired contact easily.
Contiene prácticos botones para llamar o mensajear a tus contactos. Todos los campos visibles pueden ser personalizados a tu gusto, y fácilmente puedes ocultar los que no uses. La función de búsqueda buscará el texto dado en cada campo de contacto visible, para hacer más fácil encontrar el contacto deseado.
There is a lightweight dialpad at your service too, with smart contact suggestions.
También tiene un ligero teclado de marcado a tu servicio, con sugerencias de contactos inteligentes.
It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
Soporta exportar/importar contactos en formato vCard a archivos .vcf, para fácil migración o respaldo de tu información.
With this modern and stable contacts manager you can protect your contacts by not sharing them with other apps, so you can keep your contacts private.
Con este moderno y estable gestor de contactos puedes proteger tus contactos no compartiéndolos con otras aplicaciones, así puedes mantener tus contactos privados.
Like the contact source, you can also easily change the contact name, email, phone number, address, organization, groups and many other customizable fields. You can use it for storing contact events too, like birthdays, anniversaries, or any other custom ones.
Como el origen del contacto, fácilmente también puedes cambiar el nombre del contacto, correo electrónico, número de teléfono, dirección, organización, grupos y muchos otros campos personalizables. Puedes usarlo para almacenar eventos de contactos también, como cumpleaños, aniversarios, o cualqueir otro personalizado.
This simple contact editor has many handy settings like showing phone numbers on the main screen, toggle contact thumbnail visibility, showing only contacts with phone numbers, showing a call confirmation dialog before initiating a call. It comes with a quick dialer that also makes use of letters.
Este simple editor de contactos tiene varios ajustes prácticos como mostrar números de teléfono en la pantalla principal, alternar entre mostrar la miniatura del contacto, mostrar sólamente contactos con números de teléfono, mostrar un diálogo de confirmar llamada antes de iniciarla. Incluye un marcador rápido que también hace uso de las letras.
To further improve the user experience, you can customize what happens at clicking on a contact. You can either initiate a call, go to the View Details screen, or edit the selected contact.
Para mejorar la experiencia de usuario aún más, puedes personalizar lo que pasa al tocar un contacto. Puedes iniciar una llamada, ir a la Vista de detalles o editar el contacto seleccionado.
You can easily block phone numbers to avoid unwanted incoming calls.
Puedes bloquear números de teléfono fácilmente para evitar llamadas indeseadas.
To avoid showing potentially unwanted contacts, it has a powerful built in duplicate contact merger.
Para evitar mosrar contactos potencialmente indeseados, tiene un poderoso fusionador de contactos duplicados.
It comes with material design and dark theme by default, provides great user experience for easy usage. The lack of internet access gives you more privacy, security and stability than other apps.
Incluye diseño material y tema obscuro por defecto, provee una gran experiencia de usuario para uso sencillo. La falta del acceso a internet te da más privacidad, seguridad y estabilidad que otras aplicaciones.
No contiene anuncios o permisos innecesarios. Es complétamente de código abierto, provee colores personalziables.
No contiene anuncios o permisos innecesarios. Es completamente de código abierto, provee colores personalizables.
<b>Check out the full suite of Simple Tools here:</b>
<b>Mira la suite completa de herramientas Simples aquí:</b>
https://www.simplemobiletools.com
<b>Standalone website of Simple Contacts Pro:</b>

View File

@ -105,6 +105,7 @@
<string name="groups">Taldeak</string>
<string name="contact_source">Kontaktu jatorria</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -2,17 +2,17 @@
<string name="app_name">Simple Contacts</string>
<string name="app_launcher_name">Yhteystiedot</string>
<string name="address">Osoite</string>
<string name="inserting">Sijoitetaan</string>
<string name="updating">Päivitetään</string>
<string name="phone_storage_hidden">Puhelimen muisti piilotettu</string>
<string name="inserting">Sijoitetaan...</string>
<string name="updating">Päivitetään...</string>
<string name="phone_storage_hidden">Puhelimen muisti (piilotettu muilta sovelluksilta)</string>
<string name="company">Yritys</string>
<string name="job_position">Ammatti</string>
<string name="website">Internet-sivu</string>
<string name="website">Verkkosivu</string>
<string name="send_sms_to_contacts">Lähetä tekstiviesti yhteystiedoille</string>
<string name="send_email_to_contacts">Lähetä sähköposti yhteystiedoille</string>
<string name="send_sms_to_group">Lähetä tekstiviesti ryhmälle</string>
<string name="send_email_to_group">Lähetä sähköposti ryhmälle</string>
<string name="call_person">Soita</string>
<string name="call_person">Soita %s</string>
<string name="create_new_contact">Luo uusi yhteystieto</string>
<string name="add_to_existing_contact">Lisää olemassa olevaan yhteystietoon</string>
@ -30,8 +30,8 @@
<string name="create_new_group">Luo uusi ryhmä</string>
<string name="remove_from_group">Poista ryhmästä</string>
<string name="no_group_participants">Ryhmä on tyhjä</string>
<string name="add_contacts">Lisää yhteystietoja</string>
<string name="no_group_created">Ryhmää ei luotu</string>
<string name="add_contacts">Lisää yhteystietoihin</string>
<string name="no_group_created">Laitteessa ei ole yhteistietoryhmiä</string>
<string name="create_group">Luo ryhmä</string>
<string name="add_to_group">Lisää ryhmään</string>
<string name="create_group_under_account">Luo ryhmä tilille</string>
@ -42,16 +42,16 @@
<string name="remove_photo">Poista kuva</string>
<!-- Settings -->
<string name="show_phone_numbers">Näytä puhelinnumerot</string>
<string name="show_phone_numbers">Näytä puhelinnumerot päänäytöllä</string>
<string name="show_contact_thumbnails">Näytä yhteystietojen kuvakkeet</string>
<string name="show_dialpad_button">Näytä soittimen painike</string>
<string name="on_contact_click">Yhteystietoa painaessa</string>
<string name="show_dialpad_button">Näytä numeronvalitsin-painike päänäytöllä</string>
<string name="on_contact_click">Yhteystietoa painaessa</string>
<string name="call_contact">Soita</string>
<string name="view_contact">Näytä yhteystieto</string>
<string name="manage_shown_contact_fields">Valitse, mitkä yhteystietojen kentät näytetään</string>
<string name="manage_shown_tabs">Valitse sovelluksen välilehdet</string>
<string name="contacts">Yhteystiedot</string>
<string name="show_call_confirmation_dialog">Näytä puhelun vahvistusruutu</string>
<string name="show_call_confirmation_dialog">Näytä soiton vahvistusruutu ennen soittoa</string>
<string name="show_only_contacts_with_numbers">Näytä ainoastaan numerolliset yhteystiedot</string>
<string name="show_private_contacts">Näytä yksityiset yhteystiedot sovelluksissa Simple Dialer, Simple SMS Messenger ja Simple Calendar Pro</string>
@ -62,9 +62,9 @@
<!-- Phone numbers -->
<string name="mobile">Matkapuhelin</string>
<string name="main_number">Päänumero</string>
<string name="work_fax">Työfaxi</string>
<string name="home_fax">Kotifaxi</string>
<string name="main_number">Oletus</string>
<string name="work_fax">Faksi (työ)</string>
<string name="home_fax">Faksi (koti)</string>
<string name="pager">Hakulaite</string>
<string name="no_phone_number_found">Puhelinnumeroa ei löytynyt</string>
@ -73,7 +73,7 @@
<string name="anniversary">Vuosipäivä</string>
<!-- Favorites -->
<string name="no_favorites">Ei suosikkeja</string>
<string name="no_favorites">Näyttää siltä, että et ole vielä lisännyt suosikkiyhteystietoja.</string>
<!-- Search -->
<string name="search_contacts">Etsi yhteystietoja</string>
@ -83,46 +83,47 @@
<!-- Export / Import -->
<string name="import_contacts">Tuo yhteystietoja</string>
<string name="export_contacts">Vie yhteystietoja</string>
<string name="import_contacts_from_vcf">Tuo .vcf yhteystietoja</string>
<string name="export_contacts_to_vcf">Vie .vcf yhteystietoja</string>
<string name="target_contact_source">Kohdenna yhteystiedon lähde</string>
<string name="import_contacts_from_vcf">Tuo yhteystietoja .vcf-tiedostosta</string>
<string name="export_contacts_to_vcf">Vie yhteystietoja .vcf-tiedostoon</string>
<string name="target_contact_source">Yhteystietojen lähde</string>
<string name="include_contact_sources">Sisällytä yhteystiedon lähteet</string>
<string name="filename_without_vcf">Tiedostonimi ilman .vcf-päätettä</string>
<string name="filename_without_vcf">Tiedostonimi (ilman .vcf-päätettä)</string>
<!-- Dialpad -->
<string name="dialpad">Näppäimistö</string>
<string name="dialpad">Numeronvalitsin</string>
<string name="add_number_to_contact">Lisää numero yhteystietoihin</string>
<!-- Visible fields -->
<string name="prefix">Alku</string>
<string name="suffix">Pääte</string>
<string name="phone_numbers">Puhelinnumero</string>
<string name="prefix">Etuliite</string>
<string name="suffix">Jälkiliite</string>
<string name="phone_numbers">Puhelinnumerot</string>
<string name="emails">Sähköpostit</string>
<string name="addresses">Osoitteet</string>
<string name="events">Tapahtumat</string>
<string name="organization">Organisaatio</string>
<string name="websites">Internetsivut</string>
<string name="events">Tapahtumat (syntymäpäivät, vuosipäivät)</string>
<string name="organization">Yritys</string>
<string name="websites">Verkkosivut</string>
<string name="groups">Ryhmät</string>
<string name="contact_source">Yhteystiedon lähde</string>
<string name="instant_messaging">Pikaviestin</string>
<string name="ringtone">Soittoääni</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Yhteystieto poistetaan kaikista lähteistä.</string>
<plurals name="delete_groups">
<item quantity="one">%d ryhmä</item>
<item quantity="one">%d ryhmän</item>
<item quantity="other">%d ryhmää</item>
</plurals>
<!-- FAQ -->
<string name="faq_1_title">Voinko valita, mitkä täytettävät kentät näytetään yhteystiedon ominaisuuksissa?</string>
<string name="faq_1_text">Kyllä. Mene asetuksiin -> Valitse täytettävät kentät. Voit valita, mitkä kentät näytetään. Osa kentistä on oletuksena piilotettuna, , joten saatat löytää joitain uusia.</string>
<string name="faq_1_title">Voinko valita, mitkä täytettävät kentät näytetään yhteystiedon ominaisuuksissa?</string>
<string name="faq_1_text">Kyllä. Mene asetuksiin -> Valitse täytettävät kentät. Voit valita, mitkä kentät näytetään. Osa kentistä on oletuksena piilotettuna, joten saatat löytää joitain uusia.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Contacts Pro - Manage your contacts easily</string>
<string name="app_title">Simple Contacts Pro - Yhteystietojen hallinta</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Easy and quick contact management with no ads, handles groups and favorites too.</string>
<string name="app_short_description">Helppo ja nopea yhteystietojen hallinta ilman mainoksia. Hallitsee myös ryhmät ja suosikit.</string>
<string name="app_long_description">
A lightweight app for managing your contacts loved by millions of people. The contacts can be stored on your device only, but also synchronized via Google, or other accounts.

View File

@ -103,6 +103,7 @@
<string name="groups">Groupe</string>
<string name="contact_source">Source du contact</string>
<string name="instant_messaging">Messagerie instantanée (MI)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Le contact sera supprimé de toutes les sources de contacts.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grupe</string>
<string name="contact_source">Izvori kontakata</string>
<string name="instant_messaging">Brzo slanje poruka (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Csoportok</string>
<string name="contact_source">Névjegy account</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grup</string>
<string name="contact_source">Sumber kontak</string>
<string name="instant_messaging">Pesan singkat (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Kontak akan dihapus dari semua sumber kontak.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grup</string>
<string name="contact_source">Sumber kontak</string>
<string name="instant_messaging">Pesan singkat (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Kontak akan dihapus dari semua sumber kontak.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Gruppi</string>
<string name="contact_source">Provenienza del contatto</string>
<string name="instant_messaging">Messaggistica istantanea (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -1,5 +1,5 @@
<resources>
<string name="app_name">Simple Contacts</string>
<string name="app_name">Simpleコンタクト</string>
<string name="app_launcher_name">連絡先</string>
<string name="address">住所</string>
<string name="inserting">挿入中…</string>
@ -12,7 +12,7 @@
<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="call_person">%sに発信</string>
<string name="create_new_contact">新しい連絡先を作成</string>
<string name="add_to_existing_contact">既存の連絡先に追加</string>
@ -53,7 +53,7 @@
<string name="contacts">連絡先</string>
<string name="show_call_confirmation_dialog">発信する前に確認ダイアログを表示する</string>
<string name="show_only_contacts_with_numbers">電話番号が登録された連絡先のみ表示する</string>
<string name="show_private_contacts">Show private contacts to Simple Dialer, Simple SMS Messenger and Simple Calendar Pro</string>
<string name="show_private_contacts">Simpleダイヤラー、Simpleショートメール、SimpleカレンダーProに個人の連絡先を表示</string>
<!-- Emails -->
<string name="home">自宅</string>
@ -62,7 +62,7 @@
<!-- Phone numbers -->
<string name="mobile">携帯</string>
<string name="main_number">Main</string>
<string name="main_number">メイン</string>
<string name="work_fax">職場FAX</string>
<string name="home_fax">自宅FAX</string>
<string name="pager">ポケベル</string>
@ -78,15 +78,15 @@
<!-- Search -->
<string name="search_contacts">連絡先を検索</string>
<string name="search_favorites">お気に入りを検索</string>
<string name="search_groups">Search groups</string>
<string name="search_groups">グループを検索</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="target_contact_source">対象となる連絡先のソース</string>
<string name="include_contact_sources">連絡先のソースを含める</string>
<string name="filename_without_vcf">ファイル名 (.vcfを含めない)</string>
<!-- Dialpad -->
@ -104,14 +104,15 @@
<string name="websites">ウェブサイト</string>
<string name="groups">グループ</string>
<string name="contact_source">インポート元</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="instant_messaging">インスタントメッセージ(IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>
<string name="delete_from_all_sources">全ての連絡先ソースから連絡先を消去します</string>
<plurals name="delete_groups">
<item quantity="one">%d group</item>
<item quantity="other">%d groups</item>
<item quantity="one">%d件のグループ</item>
<item quantity="other">%d件のグループ</item>
</plurals>
<!-- FAQ -->

View File

@ -105,6 +105,7 @@
<string name="groups">Groups</string>
<string name="contact_source">Contact source</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grupės</string>
<string name="contact_source">Kontakto šaltinis</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">ഗ്രൂപ്പുകൾ</string>
<string name="contact_source">കോൺ‌ടാക്റ്റ് ഉറവിടം</string>
<string name="instant_messaging">തത്സമയ സന്ദേശം അയക്കൽ</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">എല്ലാ കോൺ‌ടാക്റ്റ് ഉറവിടങ്ങളിൽ‌ നിന്നും ഈ കോൺ‌ടാക്റ്റ് നീക്കംചെയ്യും.</string>

View File

@ -65,7 +65,7 @@
<string name="main_number">Standaard</string>
<string name="work_fax">Fax Werk</string>
<string name="home_fax">Fax Thuis</string>
<string name="pager">Pager</string>
<string name="pager">Pieper</string>
<string name="no_phone_number_found">Geen telefoonnummers gevonden</string>
<!-- Events -->
@ -105,6 +105,7 @@
<string name="groups">Groepen</string>
<string name="contact_source">Account</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">De contactpersoon zal worden verwijderd uit alle accounts.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grupy</string>
<string name="contact_source">Miejsce przechowywania kontaktu</string>
<string name="instant_messaging">Komunikator</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grupos</string>
<string name="contact_source">Origem do contato</string>
<string name="instant_messaging">Mensageiro instantâneo (MI)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">O contato será removido de todas removido de todas as fontes de contato.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grupos</string>
<string name="contact_source">Origem do contacto</string>
<string name="instant_messaging">Mensagem instantânea (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">O contacto será apagado de todas as origens.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Группы</string>
<string name="contact_source">Источник контакта</string>
<string name="instant_messaging">Мессенджеры</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Контакт будет удалён из всех источников контактов.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Skupiny</string>
<string name="contact_source">Zdroje kontaktov</string>
<string name="instant_messaging">Rýchle správy (IM)</string>
<string name="ringtone">Zvonenie</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Kontakt bude vymazaný zo všetkých zdrojov kontaktov.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Grupper</string>
<string name="contact_source">Kontaktkälla</string>
<string name="instant_messaging">Snabbmeddelanden (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Kontakten kommer att tas bort från alla kontaktkällor.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Gruplar</string>
<string name="contact_source">Kişi kaynağı</string>
<string name="instant_messaging">Anlık mesajlaşma (IM)</string>
<string name="ringtone">Zil sesi</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">Kişi tüm kişi kaynaklarından kaldırılacak.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Групи</string>
<string name="contact_source">Походження контакту</string>
<string name="instant_messaging">Мессенджери</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

@ -105,6 +105,7 @@
<string name="groups">群组</string>
<string name="contact_source">联系人来源</string>
<string name="instant_messaging">即时通讯 (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">该联系人将会被从所有联系人来源移除。</string>

View File

@ -105,6 +105,7 @@
<string name="groups">群組</string>
<string name="contact_source">聯絡人來源</string>
<string name="instant_messaging">即時通訊 (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">這聯絡人將從全部通訊錄來源移除。</string>

View File

@ -105,6 +105,7 @@
<string name="groups">Groups</string>
<string name="contact_source">Contact source</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<string name="ringtone">Ringtone</string>
<!-- Confirmation dialog -->
<string name="delete_from_all_sources">The contact will be removed from all contact sources.</string>

View File

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

View File

@ -0,0 +1 @@
* Added some translation, stability and UX improvements

View File

@ -0,0 +1,4 @@
* Always fetch the same contacts, no matter what sorting is set
* Fix an organization updating related issue
* Merge names into 1 line at the View Details screen
* Improve Threema contact handling

View File

@ -0,0 +1,4 @@
* Allow customizing the ringtone per contact (Ringtone has to be enabled as a customizable field in the app settings)
* Added a White theme with special handling
* Added some letter fastscroller related improvements
* Some performance, stability and translation related improvements

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@ -1,6 +1,6 @@
#Thu Jun 18 23:08:40 CEST 2020
#Tue Nov 03 22:18:17 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip