diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae243527..7227b9c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,26 @@
Changelog
==========
+Version 6.9.0 *(2020-04-16)*
+----------------------------
+
+ * Use colored contact avatars with the first letter of their name to make the app happier :)
+ * Properly recognize the local Phone contact source, even when empty
+ * Fix some glitches related to the letters in the fastscroller not being correctly sorted
+ * Couple other stability, translation and UI improvements
+
+Version 6.8.0 *(2020-03-18)*
+----------------------------
+
+ * Remember the last used folder at contacts exporting
+ * Do not request the Storage permission on Android 10+, use Scoped Storage
+
+Version 6.7.1 *(2020-03-08)*
+----------------------------
+
+ * Fixed some letter scrollbar related glitches
+ * Added some translation and stability related improvements
+
Version 6.7.0 *(2020-02-05)*
----------------------------
diff --git a/README.md b/README.md
index b5575b39..b8cd92e0 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# Simple Contacts
-
+
A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchronized via Google, or other accounts. You can display your favorite contacts on a separate list.
diff --git a/app/build.gradle b/app/build.gradle
index 3a620072..9f791167 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,14 +11,14 @@ if (keystorePropertiesFile.exists()) {
android {
compileSdkVersion 29
- buildToolsVersion "29.0.2"
+ buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.simplemobiletools.contacts.pro"
minSdkVersion 21
targetSdkVersion 29
- versionCode 57
- versionName "6.7.0"
+ versionCode 60
+ versionName "6.9.0"
setProperty("archivesBaseName", "contacts")
}
@@ -57,13 +57,13 @@ android {
}
dependencies {
- implementation 'com.simplemobiletools:commons:5.22.10'
+ implementation 'com.simplemobiletools:commons:5.27.1'
implementation 'joda-time:joda-time:2.10.1'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.5'
- implementation 'com.github.tibbi:IndicatorFastScroll:d615a5c141'
+ implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a'
- kapt "androidx.room:room-compiler:2.2.2"
- implementation "androidx.room:room-runtime:2.2.2"
- annotationProcessor "androidx.room:room-compiler:2.2.2"
+ kapt "androidx.room:room-compiler:2.2.5"
+ implementation "androidx.room:room-runtime:2.2.5"
+ annotationProcessor "androidx.room:room-compiler:2.2.5"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6eade4d5..eb8dfaa4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -9,13 +9,17 @@
-
+
+
+
@@ -60,6 +63,12 @@
+
+
@@ -257,6 +266,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
callStarted()
+ Call.STATE_DISCONNECTED -> endCall()
+ Call.STATE_CONNECTING -> initOutgoingCallUI()
+ }
+
+ if (state == Call.STATE_DISCONNECTED || state == Call.STATE_DISCONNECTING) {
+ callTimer.cancel()
+ }
+
+ val statusTextId = when (state) {
+ Call.STATE_RINGING -> R.string.is_calling
+ Call.STATE_DIALING -> R.string.dialing
+ else -> 0
+ }
+
+ if (statusTextId != 0) {
+ call_status_label.text = getString(statusTextId)
+ }
+
+ setupNotification()
+ }
+
+ private fun acceptCall() {
+ CallManager.accept()
+ }
+
+ private fun initOutgoingCallUI() {
+ incoming_call_holder.beGone()
+ ongoing_call_holder.beVisible()
+ }
+
+ private fun callStarted() {
+ incoming_call_holder.beGone()
+ ongoing_call_holder.beVisible()
+ audioManager.mode = AudioManager.MODE_IN_CALL
+ callTimer.scheduleAtFixedRate(getCallTimerUpdateTask(), 1000, 1000)
+ }
+
+ private fun endCall() {
+ CallManager.reject()
+ if (proximityWakeLock?.isHeld == true) {
+ proximityWakeLock!!.release()
+ }
+
+ audioManager.mode = AudioManager.MODE_NORMAL
+ if (isCallEnded) {
+ finish()
+ return
+ }
+
+ isCallEnded = true
+ if (callDuration > 0) {
+ runOnUiThread {
+ call_status_label.text = "${callDuration.getFormattedDuration()} (${getString(R.string.call_ended)})"
+ Handler().postDelayed({
+ finish()
+ }, 3000)
+ }
+ } else {
+ call_status_label.text = getString(R.string.call_ended)
+ finish()
+ }
+ }
+
+ private fun getCallTimerUpdateTask() = object : TimerTask() {
+ override fun run() {
+ callDuration++
+ runOnUiThread {
+ if (!isCallEnded) {
+ call_status_label.text = callDuration.getFormattedDuration()
+ }
+ }
+ }
+ }
+
+ @SuppressLint("NewApi")
+ private val callCallback = object : Call.Callback() {
+ override fun onStateChanged(call: Call, state: Int) {
+ super.onStateChanged(call, state)
+ updateCallState(state)
+ }
+ }
+
+ @SuppressLint("NewApi")
+ private fun addLockScreenFlags() {
+ if (isOreoMr1Plus()) {
+ setShowWhenLocked(true)
+ setTurnScreenOn(true)
+ } else {
+ window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
+ }
+
+ if (isOreoPlus()) {
+ (getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager).requestDismissKeyguard(this, null)
+ } else {
+ window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD)
+ }
+ }
+
+ private fun initProximitySensor() {
+ val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
+ proximityWakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "com.simplemobiletools.contacts.pro:wake_lock")
+ proximityWakeLock!!.acquire(10 * MINUTE_SECONDS * 1000L)
+ }
+
+ @SuppressLint("NewApi")
+ private fun setupNotification() {
+ val callState = CallManager.getState()
+ val channelId = "simple_contacts_call"
+ if (isOreoPlus()) {
+ val importance = NotificationManager.IMPORTANCE_DEFAULT
+ val name = "call_notification_channel"
+
+ NotificationChannel(channelId, name, importance).apply {
+ notificationManager.createNotificationChannel(this)
+ }
+ }
+
+ val openAppIntent = Intent(this, CallActivity::class.java)
+ openAppIntent.flags = Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
+ val openAppPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, 0)
+
+ val acceptCallIntent = Intent(this, CallActionReceiver::class.java)
+ acceptCallIntent.action = ACCEPT_CALL
+ val acceptPendingIntent = PendingIntent.getBroadcast(this, 0, acceptCallIntent, PendingIntent.FLAG_CANCEL_CURRENT)
+
+ val declineCallIntent = Intent(this, CallActionReceiver::class.java)
+ declineCallIntent.action = DECLINE_CALL
+ val declinePendingIntent = PendingIntent.getBroadcast(this, 1, declineCallIntent, PendingIntent.FLAG_CANCEL_CURRENT)
+
+ val callerName = if (callContact != null && callContact!!.name.isNotEmpty()) callContact!!.name else getString(R.string.unknown_caller)
+ val contentTextId = when (callState) {
+ Call.STATE_RINGING -> R.string.is_calling
+ Call.STATE_DIALING -> R.string.dialing
+ Call.STATE_DISCONNECTED -> R.string.call_ended
+ Call.STATE_DISCONNECTING -> R.string.call_ending
+ else -> R.string.ongoing_call
+ }
+
+ val collapsedView = RemoteViews(packageName, R.layout.call_notification).apply {
+ setText(R.id.notification_caller_name, callerName)
+ setText(R.id.notification_call_status, getString(contentTextId))
+ setVisibleIf(R.id.notification_accept_call, callState == Call.STATE_RINGING)
+
+ setOnClickPendingIntent(R.id.notification_decline_call, declinePendingIntent)
+ setOnClickPendingIntent(R.id.notification_accept_call, acceptPendingIntent)
+
+ if (callContactAvatar != null) {
+ setImageViewBitmap(R.id.notification_thumbnail, getCircularBitmap(callContactAvatar!!))
+ }
+ }
+
+ val builder = NotificationCompat.Builder(this, channelId)
+ .setSmallIcon(R.drawable.ic_phone_vector)
+ .setContentIntent(openAppPendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setCategory(Notification.CATEGORY_CALL)
+ .setCustomContentView(collapsedView)
+ .setOngoing(true)
+ .setUsesChronometer(callState == Call.STATE_ACTIVE)
+ .setChannelId(channelId)
+ .setStyle(NotificationCompat.DecoratedCustomViewStyle())
+
+ val notification = builder.build()
+ notificationManager.notify(CALL_NOTIFICATION_ID, notification)
+ }
+
+ @SuppressLint("NewApi")
+ private fun getCallContactAvatar(): Bitmap? {
+ var bitmap: Bitmap? = null
+ if (callContact?.photoUri?.isNotEmpty() == true) {
+ val photoUri = Uri.parse(callContact!!.photoUri)
+ bitmap = if (isQPlus()) {
+ val tmbSize = resources.getDimension(R.dimen.contact_icons_size).toInt()
+ contentResolver.loadThumbnail(photoUri, Size(tmbSize, tmbSize), null)
+ } else {
+ MediaStore.Images.Media.getBitmap(contentResolver, photoUri)
+ }
+
+ bitmap = getCircularBitmap(bitmap!!)
+ }
+
+ return bitmap
+ }
+
+ private fun getCircularBitmap(bitmap: Bitmap): Bitmap {
+ val output = Bitmap.createBitmap(bitmap.width, bitmap.width, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(output)
+ val paint = Paint()
+ val rect = Rect(0, 0, bitmap.width, bitmap.height)
+ val radius = bitmap.width / 2.toFloat()
+
+ paint.isAntiAlias = true
+ canvas.drawARGB(0, 0, 0, 0)
+ canvas.drawCircle(radius, radius, radius, paint)
+ paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
+ canvas.drawBitmap(bitmap, rect, rect, paint)
+ return output
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt
index cdb316b8..15a03894 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt
@@ -1,9 +1,10 @@
package com.simplemobiletools.contacts.pro.activities
import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
-import android.provider.ContactsContract
+import android.provider.ContactsContract.CommonDataKinds.*
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
@@ -15,12 +16,9 @@ import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
-import com.simplemobiletools.commons.extensions.applyColorFilter
-import com.simplemobiletools.commons.extensions.getColoredDrawableWithColor
-import com.simplemobiletools.commons.extensions.getContrastColor
+import com.simplemobiletools.commons.extensions.getContactLetterIcon
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.pro.R
-import com.simplemobiletools.contacts.pro.extensions.config
import com.simplemobiletools.contacts.pro.extensions.sendEmailIntent
import com.simplemobiletools.contacts.pro.extensions.sendSMSIntent
import com.simplemobiletools.contacts.pro.extensions.shareContacts
@@ -33,13 +31,7 @@ abstract class ContactActivity : SimpleActivity() {
protected var currentContactPhotoPath = ""
fun showPhotoPlaceholder(photoView: ImageView) {
- val background = resources.getDrawable(R.drawable.contact_circular_background)
- background.applyColorFilter(config.primaryColor)
- photoView.background = background
-
- val placeholder = resources.getColoredDrawableWithColor(R.drawable.ic_person_vector, config.primaryColor.getContrastColor())
- val padding = resources.getDimension(R.dimen.activity_margin).toInt()
- photoView.setPadding(padding, padding, padding, padding)
+ val placeholder = BitmapDrawable(resources, getContactLetterIcon(contact?.getNameToDisplay() ?: "S"))
photoView.setImageDrawable(placeholder)
currentContactPhotoPath = ""
contact?.photo = null
@@ -48,30 +40,29 @@ abstract class ContactActivity : SimpleActivity() {
fun updateContactPhoto(path: String, photoView: ImageView, bitmap: Bitmap? = null) {
currentContactPhotoPath = path
val options = RequestOptions()
- .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
- .centerCrop()
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
+ .centerCrop()
if (isDestroyed || isFinishing) {
return
}
Glide.with(this)
- .load(bitmap ?: path)
- .transition(DrawableTransitionOptions.withCrossFade())
- .apply(options)
- .apply(RequestOptions.circleCropTransform())
- .listener(object : RequestListener {
- override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
- photoView.setPadding(0, 0, 0, 0)
- photoView.background = ColorDrawable(0)
- return false
- }
+ .load(bitmap ?: path)
+ .transition(DrawableTransitionOptions.withCrossFade())
+ .apply(options)
+ .apply(RequestOptions.circleCropTransform())
+ .listener(object : RequestListener {
+ override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
+ photoView.background = ColorDrawable(0)
+ return false
+ }
- override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean {
- showPhotoPlaceholder(photoView)
- return true
- }
- }).into(photoView)
+ override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean {
+ showPhotoPlaceholder(photoView)
+ return true
+ }
+ }).into(photoView)
}
fun deleteContact() {
@@ -121,67 +112,67 @@ abstract class ContactActivity : SimpleActivity() {
}
fun getPhoneNumberTypeText(type: Int, label: String): String {
- return if (type == ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM) {
+ return if (type == BaseTypes.TYPE_CUSTOM) {
label
} else {
getString(when (type) {
- ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE -> R.string.mobile
- ContactsContract.CommonDataKinds.Phone.TYPE_HOME -> R.string.home
- ContactsContract.CommonDataKinds.Phone.TYPE_WORK -> R.string.work
- ContactsContract.CommonDataKinds.Phone.TYPE_MAIN -> R.string.main_number
- ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK -> R.string.work_fax
- ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME -> R.string.home_fax
- ContactsContract.CommonDataKinds.Phone.TYPE_PAGER -> R.string.pager
+ Phone.TYPE_MOBILE -> R.string.mobile
+ Phone.TYPE_HOME -> R.string.home
+ Phone.TYPE_WORK -> R.string.work
+ Phone.TYPE_MAIN -> R.string.main_number
+ Phone.TYPE_FAX_WORK -> R.string.work_fax
+ Phone.TYPE_FAX_HOME -> R.string.home_fax
+ Phone.TYPE_PAGER -> R.string.pager
else -> R.string.other
})
}
}
fun getEmailTypeText(type: Int, label: String): String {
- return if (type == ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM) {
+ return if (type == BaseTypes.TYPE_CUSTOM) {
label
} else {
getString(when (type) {
- ContactsContract.CommonDataKinds.Email.TYPE_HOME -> R.string.home
- ContactsContract.CommonDataKinds.Email.TYPE_WORK -> R.string.work
- ContactsContract.CommonDataKinds.Email.TYPE_MOBILE -> R.string.mobile
+ Email.TYPE_HOME -> R.string.home
+ Email.TYPE_WORK -> R.string.work
+ Email.TYPE_MOBILE -> R.string.mobile
else -> R.string.other
})
}
}
fun getAddressTypeText(type: Int, label: String): String {
- return if (type == ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM) {
+ return if (type == BaseTypes.TYPE_CUSTOM) {
label
} else {
getString(when (type) {
- ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME -> R.string.home
- ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK -> R.string.work
+ StructuredPostal.TYPE_HOME -> R.string.home
+ StructuredPostal.TYPE_WORK -> R.string.work
else -> R.string.other
})
}
}
fun getIMTypeText(type: Int, label: String): String {
- return if (type == ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) {
+ return if (type == Im.PROTOCOL_CUSTOM) {
label
} else {
getString(when (type) {
- ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM -> R.string.aim
- ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN -> R.string.windows_live
- ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO -> R.string.yahoo
- ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE -> R.string.skype
- ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ -> R.string.qq
- ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK -> R.string.hangouts
- ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ -> R.string.icq
+ Im.PROTOCOL_AIM -> R.string.aim
+ Im.PROTOCOL_MSN -> R.string.windows_live
+ Im.PROTOCOL_YAHOO -> R.string.yahoo
+ Im.PROTOCOL_SKYPE -> R.string.skype
+ Im.PROTOCOL_QQ -> R.string.qq
+ Im.PROTOCOL_GOOGLE_TALK -> R.string.hangouts
+ Im.PROTOCOL_ICQ -> R.string.icq
else -> R.string.jabber
})
}
}
fun getEventTextId(type: Int) = when (type) {
- ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY -> R.string.anniversary
- ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY -> R.string.birthday
+ Event.TYPE_ANNIVERSARY -> R.string.anniversary
+ Event.TYPE_BIRTHDAY -> R.string.birthday
else -> R.string.other
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialerActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialerActivity.kt
index 3cd2eac6..b05ff469 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialerActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialerActivity.kt
@@ -9,12 +9,12 @@ import android.os.Bundle
import android.telecom.PhoneAccount
import android.telecom.TelecomManager
import android.view.Menu
+import com.simplemobiletools.commons.extensions.isDefaultDialer
import com.simplemobiletools.commons.extensions.showErrorToast
+import com.simplemobiletools.commons.extensions.telecomManager
import com.simplemobiletools.commons.extensions.toast
+import com.simplemobiletools.commons.helpers.REQUEST_CODE_SET_DEFAULT_DIALER
import com.simplemobiletools.contacts.pro.R
-import com.simplemobiletools.contacts.pro.extensions.isDefaultDialer
-import com.simplemobiletools.contacts.pro.extensions.telecomManager
-import com.simplemobiletools.contacts.pro.helpers.REQUEST_CODE_SET_DEFAULT_DIALER
@TargetApi(Build.VERSION_CODES.M)
class DialerActivity : SimpleActivity() {
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialpadActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialpadActivity.kt
index ed5bb0a3..aa2c198e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialpadActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/DialpadActivity.kt
@@ -14,21 +14,22 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.REQUEST_CODE_SET_DEFAULT_DIALER
import com.simplemobiletools.commons.helpers.isOreoPlus
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.adapters.ContactsAdapter
import com.simplemobiletools.contacts.pro.dialogs.CallConfirmationDialog
import com.simplemobiletools.contacts.pro.extensions.callContact
import com.simplemobiletools.contacts.pro.extensions.config
-import com.simplemobiletools.contacts.pro.extensions.isDefaultDialer
import com.simplemobiletools.contacts.pro.extensions.startCallIntent
import com.simplemobiletools.contacts.pro.helpers.ContactsHelper
import com.simplemobiletools.contacts.pro.helpers.KEY_PHONE
import com.simplemobiletools.contacts.pro.helpers.LOCATION_DIALPAD
-import com.simplemobiletools.contacts.pro.helpers.REQUEST_CODE_SET_DEFAULT_DIALER
import com.simplemobiletools.contacts.pro.models.Contact
import com.simplemobiletools.contacts.pro.models.SpeedDial
import kotlinx.android.synthetic.main.activity_dialpad.*
+import kotlinx.android.synthetic.main.activity_dialpad.dialpad_holder
+import kotlinx.android.synthetic.main.dialpad.*
class DialpadActivity : SimpleActivity() {
private var contacts = ArrayList()
@@ -105,11 +106,14 @@ class DialpadActivity : SimpleActivity() {
return true
}
- private fun checkDialIntent() {
- if (intent.action == Intent.ACTION_DIAL && intent.data != null && intent.dataString?.contains("tel:") == true) {
+ private fun checkDialIntent(): Boolean {
+ return if (intent.action == Intent.ACTION_DIAL && intent.data != null && intent.dataString?.contains("tel:") == true) {
val number = Uri.decode(intent.dataString).substringAfter("tel:")
dialpad_input.setText(number)
dialpad_input.setSelection(number.length)
+ true
+ } else {
+ false
}
}
@@ -164,7 +168,9 @@ class DialpadActivity : SimpleActivity() {
private fun gotContacts(newContacts: ArrayList) {
contacts = newContacts
- checkDialIntent()
+ if (!checkDialIntent() && dialpad_input.value.isEmpty()) {
+ dialpadValueChanged("")
+ }
}
@TargetApi(Build.VERSION_CODES.O)
@@ -174,7 +180,7 @@ class DialpadActivity : SimpleActivity() {
val secretCode = text.substring(4, text.length - 4)
if (isOreoPlus()) {
if (isDefaultDialer()) {
- getSystemService(TelephonyManager::class.java).sendDialerSpecialCode(secretCode)
+ getSystemService(TelephonyManager::class.java)?.sendDialerSpecialCode(secretCode)
} else {
launchSetDefaultDialerIntent()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt
index ac985174..c3039ba5 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt
@@ -9,6 +9,7 @@ import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract.CommonDataKinds
+import android.provider.ContactsContract.CommonDataKinds.*
import android.provider.MediaStore
import android.view.Menu
import android.view.MenuItem
@@ -29,6 +30,9 @@ import com.simplemobiletools.contacts.pro.dialogs.SelectGroupsDialog
import com.simplemobiletools.contacts.pro.extensions.*
import com.simplemobiletools.contacts.pro.helpers.*
import com.simplemobiletools.contacts.pro.models.*
+import com.simplemobiletools.contacts.pro.models.Email
+import com.simplemobiletools.contacts.pro.models.Event
+import com.simplemobiletools.contacts.pro.models.Organization
import kotlinx.android.synthetic.main.activity_edit_contact.*
import kotlinx.android.synthetic.main.item_edit_address.view.*
import kotlinx.android.synthetic.main.item_edit_email.view.*
@@ -209,10 +213,6 @@ class EditContactActivity : ContactActivity() {
contact_start_call.beVisibleIf(contact!!.phoneNumbers.isNotEmpty())
contact_send_email.beVisibleIf(contact!!.emails.isNotEmpty())
- val background = resources.getDrawable(R.drawable.contact_circular_background)
- background.applyColorFilter(config.primaryColor)
- contact_photo.background = background
-
if (contact!!.photoUri.isEmpty() && contact!!.photo == null) {
showPhotoPlaceholder(contact_photo)
} else {
@@ -220,36 +220,22 @@ class EditContactActivity : ContactActivity() {
}
val textColor = config.textColor
- contact_send_sms.applyColorFilter(textColor)
- contact_start_call.applyColorFilter(textColor)
- contact_send_email.applyColorFilter(textColor)
- contact_name_image.applyColorFilter(textColor)
- contact_numbers_image.applyColorFilter(textColor)
- contact_emails_image.applyColorFilter(textColor)
- contact_addresses_image.applyColorFilter(textColor)
- contact_ims_image.applyColorFilter(textColor)
- contact_events_image.applyColorFilter(textColor)
- contact_notes_image.applyColorFilter(textColor)
- contact_organization_image.applyColorFilter(textColor)
- contact_websites_image.applyColorFilter(textColor)
- contact_groups_image.applyColorFilter(textColor)
- contact_source_image.applyColorFilter(textColor)
+ arrayOf(contact_send_sms, contact_start_call, contact_send_email, 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 {
+ it.applyColorFilter(textColor)
+ }
val adjustedPrimaryColor = getAdjustedPrimaryColor()
- contact_numbers_add_new.applyColorFilter(adjustedPrimaryColor)
- contact_numbers_add_new.background.applyColorFilter(textColor)
- contact_emails_add_new.applyColorFilter(adjustedPrimaryColor)
- contact_emails_add_new.background.applyColorFilter(textColor)
- contact_addresses_add_new.applyColorFilter(adjustedPrimaryColor)
- contact_addresses_add_new.background.applyColorFilter(textColor)
- contact_ims_add_new.applyColorFilter(adjustedPrimaryColor)
- contact_ims_add_new.background.applyColorFilter(textColor)
- contact_events_add_new.applyColorFilter(adjustedPrimaryColor)
- contact_events_add_new.background.applyColorFilter(textColor)
- contact_websites_add_new.applyColorFilter(adjustedPrimaryColor)
- contact_websites_add_new.background.applyColorFilter(textColor)
- contact_groups_add_new.applyColorFilter(adjustedPrimaryColor)
- contact_groups_add_new.background.applyColorFilter(textColor)
+ arrayOf(contact_numbers_add_new, contact_emails_add_new, contact_addresses_add_new, contact_ims_add_new, contact_events_add_new,
+ contact_websites_add_new, contact_groups_add_new).forEach {
+ it.applyColorFilter(adjustedPrimaryColor)
+ }
+
+ arrayOf(contact_numbers_add_new.background, contact_emails_add_new.background, contact_addresses_add_new.background, contact_ims_add_new.background,
+ contact_events_add_new.background, contact_websites_add_new.background, contact_groups_add_new.background).forEach {
+ it.applyColorFilter(textColor)
+ }
contact_toggle_favorite.setOnClickListener { toggleFavorite() }
contact_photo.setOnClickListener { trySetPhoto() }
@@ -315,8 +301,8 @@ class EditContactActivity : ContactActivity() {
Intent("com.android.camera.action.CROP").apply {
setDataAndType(imageUri, "image/*")
putExtra(MediaStore.EXTRA_OUTPUT, lastPhotoIntentUri)
- putExtra("outputX", 720)
- putExtra("outputY", 720)
+ putExtra("outputX", 512)
+ putExtra("outputY", 512)
putExtra("aspectX", 1)
putExtra("aspectY", 1)
putExtra("crop", "true")
@@ -601,6 +587,18 @@ class EditContactActivity : ContactActivity() {
getPublicContactSource(contact!!.source) {
contact_source.text = it
}
+
+ // if the last used contact source is not available anymore, use the first available one. Could happen at ejecting SIM card
+ ContactsHelper(this).getSaveableContactSources { sources ->
+ val sourceNames = sources.map { it.name }
+ if (!sourceNames.contains(originalContactSource)) {
+ originalContactSource = sourceNames.first()
+ contact?.source = originalContactSource
+ getPublicContactSource(contact!!.source) {
+ contact_source.text = it
+ }
+ }
+ }
}
private fun setupTypePickers() {
@@ -743,20 +741,20 @@ class EditContactActivity : ContactActivity() {
private fun showNumberTypePicker(numberTypeField: TextView) {
val items = arrayListOf(
- RadioItem(CommonDataKinds.Phone.TYPE_MOBILE, getString(R.string.mobile)),
- RadioItem(CommonDataKinds.Phone.TYPE_HOME, getString(R.string.home)),
- RadioItem(CommonDataKinds.Phone.TYPE_WORK, getString(R.string.work)),
- RadioItem(CommonDataKinds.Phone.TYPE_MAIN, getString(R.string.main_number)),
- RadioItem(CommonDataKinds.Phone.TYPE_FAX_WORK, getString(R.string.work_fax)),
- RadioItem(CommonDataKinds.Phone.TYPE_FAX_HOME, getString(R.string.home_fax)),
- RadioItem(CommonDataKinds.Phone.TYPE_PAGER, getString(R.string.pager)),
- RadioItem(CommonDataKinds.Phone.TYPE_OTHER, getString(R.string.other)),
- RadioItem(CommonDataKinds.Phone.TYPE_CUSTOM, getString(R.string.custom))
+ RadioItem(Phone.TYPE_MOBILE, getString(R.string.mobile)),
+ RadioItem(Phone.TYPE_HOME, getString(R.string.home)),
+ RadioItem(Phone.TYPE_WORK, getString(R.string.work)),
+ RadioItem(Phone.TYPE_MAIN, getString(R.string.main_number)),
+ RadioItem(Phone.TYPE_FAX_WORK, getString(R.string.work_fax)),
+ RadioItem(Phone.TYPE_FAX_HOME, getString(R.string.home_fax)),
+ RadioItem(Phone.TYPE_PAGER, getString(R.string.pager)),
+ RadioItem(Phone.TYPE_OTHER, getString(R.string.other)),
+ RadioItem(Phone.TYPE_CUSTOM, getString(R.string.custom))
)
val currentNumberTypeId = getPhoneNumberTypeId(numberTypeField.value)
RadioGroupDialog(this, items, currentNumberTypeId) {
- if (it as Int == CommonDataKinds.Phone.TYPE_CUSTOM) {
+ if (it as Int == Phone.TYPE_CUSTOM) {
CustomLabelDialog(this) {
numberTypeField.text = it
}
@@ -768,11 +766,11 @@ class EditContactActivity : ContactActivity() {
private fun showEmailTypePicker(emailTypeField: TextView) {
val items = arrayListOf(
- RadioItem(CommonDataKinds.Email.TYPE_HOME, getString(R.string.home)),
- RadioItem(CommonDataKinds.Email.TYPE_WORK, getString(R.string.work)),
- RadioItem(CommonDataKinds.Email.TYPE_MOBILE, getString(R.string.mobile)),
- RadioItem(CommonDataKinds.Email.TYPE_OTHER, getString(R.string.other)),
- RadioItem(CommonDataKinds.Email.TYPE_CUSTOM, getString(R.string.custom))
+ RadioItem(CommonDataKinds.Email.TYPE_HOME, getString(R.string.home)),
+ RadioItem(CommonDataKinds.Email.TYPE_WORK, getString(R.string.work)),
+ RadioItem(CommonDataKinds.Email.TYPE_MOBILE, getString(R.string.mobile)),
+ RadioItem(CommonDataKinds.Email.TYPE_OTHER, getString(R.string.other)),
+ RadioItem(CommonDataKinds.Email.TYPE_CUSTOM, getString(R.string.custom))
)
val currentEmailTypeId = getEmailTypeId(emailTypeField.value)
@@ -789,15 +787,15 @@ class EditContactActivity : ContactActivity() {
private fun showAddressTypePicker(addressTypeField: TextView) {
val items = arrayListOf(
- RadioItem(CommonDataKinds.StructuredPostal.TYPE_HOME, getString(R.string.home)),
- RadioItem(CommonDataKinds.StructuredPostal.TYPE_WORK, getString(R.string.work)),
- RadioItem(CommonDataKinds.StructuredPostal.TYPE_OTHER, getString(R.string.other)),
- RadioItem(CommonDataKinds.StructuredPostal.TYPE_CUSTOM, getString(R.string.custom))
+ RadioItem(StructuredPostal.TYPE_HOME, getString(R.string.home)),
+ RadioItem(StructuredPostal.TYPE_WORK, getString(R.string.work)),
+ RadioItem(StructuredPostal.TYPE_OTHER, getString(R.string.other)),
+ RadioItem(StructuredPostal.TYPE_CUSTOM, getString(R.string.custom))
)
val currentAddressTypeId = getAddressTypeId(addressTypeField.value)
RadioGroupDialog(this, items, currentAddressTypeId) {
- if (it as Int == CommonDataKinds.StructuredPostal.TYPE_CUSTOM) {
+ if (it as Int == StructuredPostal.TYPE_CUSTOM) {
CustomLabelDialog(this) {
addressTypeField.text = it
}
@@ -809,20 +807,20 @@ class EditContactActivity : ContactActivity() {
private fun showIMTypePicker(imTypeField: TextView) {
val items = arrayListOf(
- RadioItem(CommonDataKinds.Im.PROTOCOL_AIM, getString(R.string.aim)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_MSN, getString(R.string.windows_live)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_YAHOO, getString(R.string.yahoo)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_SKYPE, getString(R.string.skype)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_QQ, getString(R.string.qq)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK, getString(R.string.hangouts)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_ICQ, getString(R.string.icq)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_JABBER, getString(R.string.jabber)),
- RadioItem(CommonDataKinds.Im.PROTOCOL_CUSTOM, getString(R.string.custom))
+ RadioItem(Im.PROTOCOL_AIM, getString(R.string.aim)),
+ RadioItem(Im.PROTOCOL_MSN, getString(R.string.windows_live)),
+ RadioItem(Im.PROTOCOL_YAHOO, getString(R.string.yahoo)),
+ RadioItem(Im.PROTOCOL_SKYPE, getString(R.string.skype)),
+ RadioItem(Im.PROTOCOL_QQ, getString(R.string.qq)),
+ RadioItem(Im.PROTOCOL_GOOGLE_TALK, getString(R.string.hangouts)),
+ RadioItem(Im.PROTOCOL_ICQ, getString(R.string.icq)),
+ RadioItem(Im.PROTOCOL_JABBER, getString(R.string.jabber)),
+ RadioItem(Im.PROTOCOL_CUSTOM, getString(R.string.custom))
)
val currentIMTypeId = getIMTypeId(imTypeField.value)
RadioGroupDialog(this, items, currentIMTypeId) {
- if (it as Int == CommonDataKinds.Im.PROTOCOL_CUSTOM) {
+ if (it as Int == Im.PROTOCOL_CUSTOM) {
CustomLabelDialog(this) {
imTypeField.text = it
}
@@ -834,9 +832,9 @@ class EditContactActivity : ContactActivity() {
private fun showEventTypePicker(eventTypeField: TextView) {
val items = arrayListOf(
- RadioItem(CommonDataKinds.Event.TYPE_ANNIVERSARY, getString(R.string.anniversary)),
- RadioItem(CommonDataKinds.Event.TYPE_BIRTHDAY, getString(R.string.birthday)),
- RadioItem(CommonDataKinds.Event.TYPE_OTHER, getString(R.string.other))
+ RadioItem(CommonDataKinds.Event.TYPE_ANNIVERSARY, getString(R.string.anniversary)),
+ RadioItem(CommonDataKinds.Event.TYPE_BIRTHDAY, getString(R.string.birthday)),
+ RadioItem(CommonDataKinds.Event.TYPE_OTHER, getString(R.string.other))
)
val currentEventTypeId = getEventTypeId(eventTypeField.value)
@@ -910,7 +908,7 @@ class EditContactActivity : ContactActivity() {
val numberHolder = contact_numbers_holder.getChildAt(i)
val number = numberHolder.contact_number.value
val numberType = getPhoneNumberTypeId(numberHolder.contact_number_type.value)
- val numberLabel = if (numberType == CommonDataKinds.Phone.TYPE_CUSTOM) numberHolder.contact_number_type.value else ""
+ val numberLabel = if (numberType == Phone.TYPE_CUSTOM) numberHolder.contact_number_type.value else ""
if (number.isNotEmpty()) {
phoneNumbers.add(PhoneNumber(number, numberType, numberLabel, number.normalizeNumber()))
@@ -942,7 +940,7 @@ class EditContactActivity : ContactActivity() {
val addressHolder = contact_addresses_holder.getChildAt(i)
val address = addressHolder.contact_address.value
val addressType = getAddressTypeId(addressHolder.contact_address_type.value)
- val addressLabel = if (addressType == CommonDataKinds.StructuredPostal.TYPE_CUSTOM) addressHolder.contact_address_type.value else ""
+ val addressLabel = if (addressType == StructuredPostal.TYPE_CUSTOM) addressHolder.contact_address_type.value else ""
if (address.isNotEmpty()) {
addresses.add(Address(address, addressType, addressLabel))
@@ -958,7 +956,7 @@ class EditContactActivity : ContactActivity() {
val IMsHolder = contact_ims_holder.getChildAt(i)
val IM = IMsHolder.contact_im.value
val IMType = getIMTypeId(IMsHolder.contact_im_type.value)
- val IMLabel = if (IMType == CommonDataKinds.Im.PROTOCOL_CUSTOM) IMsHolder.contact_im_type.value else ""
+ val IMLabel = if (IMType == Im.PROTOCOL_CUSTOM) IMsHolder.contact_im_type.value else ""
if (IM.isNotEmpty()) {
IMs.add(IM(IM, IMType, IMLabel))
@@ -1116,8 +1114,8 @@ class EditContactActivity : ContactActivity() {
private fun trySetPhoto() {
val items = arrayListOf(
- RadioItem(TAKE_PHOTO, getString(R.string.take_photo)),
- RadioItem(CHOOSE_PHOTO, getString(R.string.choose_photo))
+ RadioItem(TAKE_PHOTO, getString(R.string.take_photo)),
+ RadioItem(CHOOSE_PHOTO, getString(R.string.choose_photo))
)
if (currentContactPhotoPath.isNotEmpty() || contact!!.photo != null) {
@@ -1135,13 +1133,13 @@ class EditContactActivity : ContactActivity() {
private fun parseIntentData(data: ArrayList) {
data.forEach {
- when (it.get(CommonDataKinds.StructuredName.MIMETYPE)) {
+ when (it.get(StructuredName.MIMETYPE)) {
CommonDataKinds.Email.CONTENT_ITEM_TYPE -> parseEmail(it)
- CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE -> parseAddress(it)
+ StructuredPostal.CONTENT_ITEM_TYPE -> parseAddress(it)
CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> parseOrganization(it)
CommonDataKinds.Event.CONTENT_ITEM_TYPE -> parseEvent(it)
- CommonDataKinds.Website.CONTENT_ITEM_TYPE -> parseWebsite(it)
- CommonDataKinds.Note.CONTENT_ITEM_TYPE -> parseNote(it)
+ Website.CONTENT_ITEM_TYPE -> parseWebsite(it)
+ Note.CONTENT_ITEM_TYPE -> parseNote(it)
}
}
}
@@ -1154,9 +1152,9 @@ class EditContactActivity : ContactActivity() {
}
private fun parseAddress(contentValues: ContentValues) {
- val type = contentValues.getAsInteger(CommonDataKinds.StructuredPostal.DATA2) ?: DEFAULT_ADDRESS_TYPE
- val addressValue = contentValues.getAsString(CommonDataKinds.StructuredPostal.DATA4)
- ?: contentValues.getAsString(CommonDataKinds.StructuredPostal.DATA1) ?: return
+ val type = contentValues.getAsInteger(StructuredPostal.DATA2) ?: DEFAULT_ADDRESS_TYPE
+ val addressValue = contentValues.getAsString(StructuredPostal.DATA4)
+ ?: contentValues.getAsString(StructuredPostal.DATA1) ?: return
val address = Address(addressValue, type, "")
contact!!.addresses.add(address)
}
@@ -1175,12 +1173,12 @@ class EditContactActivity : ContactActivity() {
}
private fun parseWebsite(contentValues: ContentValues) {
- val website = contentValues.getAsString(CommonDataKinds.Website.DATA1) ?: return
+ val website = contentValues.getAsString(Website.DATA1) ?: return
contact!!.websites.add(website)
}
private fun parseNote(contentValues: ContentValues) {
- val note = contentValues.getAsString(CommonDataKinds.Note.DATA1) ?: return
+ val note = contentValues.getAsString(Note.DATA1) ?: return
contact!!.notes = note
}
@@ -1214,15 +1212,15 @@ class EditContactActivity : ContactActivity() {
}
private fun getPhoneNumberTypeId(value: String) = when (value) {
- getString(R.string.mobile) -> CommonDataKinds.Phone.TYPE_MOBILE
- getString(R.string.home) -> CommonDataKinds.Phone.TYPE_HOME
- getString(R.string.work) -> CommonDataKinds.Phone.TYPE_WORK
- getString(R.string.main_number) -> CommonDataKinds.Phone.TYPE_MAIN
- getString(R.string.work_fax) -> CommonDataKinds.Phone.TYPE_FAX_WORK
- getString(R.string.home_fax) -> CommonDataKinds.Phone.TYPE_FAX_HOME
- getString(R.string.pager) -> CommonDataKinds.Phone.TYPE_PAGER
- getString(R.string.other) -> CommonDataKinds.Phone.TYPE_OTHER
- else -> CommonDataKinds.Phone.TYPE_CUSTOM
+ getString(R.string.mobile) -> Phone.TYPE_MOBILE
+ getString(R.string.home) -> Phone.TYPE_HOME
+ getString(R.string.work) -> Phone.TYPE_WORK
+ getString(R.string.main_number) -> Phone.TYPE_MAIN
+ getString(R.string.work_fax) -> Phone.TYPE_FAX_WORK
+ getString(R.string.home_fax) -> Phone.TYPE_FAX_HOME
+ getString(R.string.pager) -> Phone.TYPE_PAGER
+ getString(R.string.other) -> Phone.TYPE_OTHER
+ else -> Phone.TYPE_CUSTOM
}
private fun getEmailTypeId(value: String) = when (value) {
@@ -1240,21 +1238,21 @@ class EditContactActivity : ContactActivity() {
}
private fun getAddressTypeId(value: String) = when (value) {
- getString(R.string.home) -> CommonDataKinds.StructuredPostal.TYPE_HOME
- getString(R.string.work) -> CommonDataKinds.StructuredPostal.TYPE_WORK
- getString(R.string.other) -> CommonDataKinds.StructuredPostal.TYPE_OTHER
- else -> CommonDataKinds.StructuredPostal.TYPE_CUSTOM
+ getString(R.string.home) -> StructuredPostal.TYPE_HOME
+ getString(R.string.work) -> StructuredPostal.TYPE_WORK
+ getString(R.string.other) -> StructuredPostal.TYPE_OTHER
+ else -> StructuredPostal.TYPE_CUSTOM
}
private fun getIMTypeId(value: String) = when (value) {
- getString(R.string.aim) -> CommonDataKinds.Im.PROTOCOL_AIM
- getString(R.string.windows_live) -> CommonDataKinds.Im.PROTOCOL_MSN
- getString(R.string.yahoo) -> CommonDataKinds.Im.PROTOCOL_YAHOO
- getString(R.string.skype) -> CommonDataKinds.Im.PROTOCOL_SKYPE
- getString(R.string.qq) -> CommonDataKinds.Im.PROTOCOL_QQ
- getString(R.string.hangouts) -> CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
- getString(R.string.icq) -> CommonDataKinds.Im.PROTOCOL_ICQ
- getString(R.string.jabber) -> CommonDataKinds.Im.PROTOCOL_JABBER
- else -> CommonDataKinds.Im.PROTOCOL_CUSTOM
+ getString(R.string.aim) -> Im.PROTOCOL_AIM
+ getString(R.string.windows_live) -> Im.PROTOCOL_MSN
+ getString(R.string.yahoo) -> Im.PROTOCOL_YAHOO
+ getString(R.string.skype) -> Im.PROTOCOL_SKYPE
+ getString(R.string.qq) -> Im.PROTOCOL_QQ
+ getString(R.string.hangouts) -> Im.PROTOCOL_GOOGLE_TALK
+ getString(R.string.icq) -> Im.PROTOCOL_ICQ
+ getString(R.string.jabber) -> Im.PROTOCOL_JABBER
+ else -> Im.PROTOCOL_CUSTOM
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt
index 4c78b4a1..a26c63cf 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt
@@ -38,8 +38,8 @@ class InsertOrEditContactActivity : SimpleActivity(), RefreshContactsListener {
private var searchMenuItem: MenuItem? = null
private val contactsFavoritesList = arrayListOf(
- CONTACTS_TAB_MASK,
- FAVORITES_TAB_MASK
+ CONTACTS_TAB_MASK,
+ FAVORITES_TAB_MASK
)
override fun onCreate(savedInstanceState: Bundle?) {
@@ -116,17 +116,17 @@ class InsertOrEditContactActivity : SimpleActivity(), RefreshContactsListener {
}
insert_or_edit_tabs_holder.onTabSelectionChanged(
- tabUnselectedAction = {
- it.icon?.applyColorFilter(config.textColor)
- },
- tabSelectedAction = {
- if (isSearchOpen) {
- getCurrentFragment()?.onSearchQueryChanged("")
- searchMenuItem?.collapseActionView()
- }
- viewpager.currentItem = it.position
- it.icon?.applyColorFilter(getAdjustedPrimaryColor())
+ tabUnselectedAction = {
+ it.icon?.applyColorFilter(config.textColor)
+ },
+ tabSelectedAction = {
+ if (isSearchOpen) {
+ getCurrentFragment()?.onSearchQueryChanged("")
+ searchMenuItem?.collapseActionView()
}
+ viewpager.currentItem = it.position
+ it.icon?.applyColorFilter(getAdjustedPrimaryColor())
+ }
)
insert_or_edit_tabs_holder.removeAllTabs()
@@ -143,7 +143,7 @@ class InsertOrEditContactActivity : SimpleActivity(), RefreshContactsListener {
insert_or_edit_tabs_holder.beVisibleIf(skippedTabs == 0)
select_contact_label?.setTextColor(getAdjustedPrimaryColor())
- new_contact_tmb?.setImageDrawable(resources.getColoredDrawableWithColor(R.drawable.ic_new_contact_vector, config.textColor))
+ new_contact_tmb?.setImageDrawable(resources.getColoredDrawableWithColor(R.drawable.ic_add_person_vector, config.textColor))
new_contact_holder?.setOnClickListener {
createNewContact()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt
index d62568a0..0eec4cbd 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt
@@ -1,12 +1,12 @@
package com.simplemobiletools.contacts.pro.activities
import android.annotation.SuppressLint
+import android.app.Activity
import android.app.SearchManager
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
-import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
@@ -43,14 +43,19 @@ import kotlinx.android.synthetic.main.fragment_contacts.*
import kotlinx.android.synthetic.main.fragment_favorites.*
import kotlinx.android.synthetic.main.fragment_groups.*
import java.io.FileOutputStream
+import java.io.OutputStream
import java.util.*
class MainActivity : SimpleActivity(), RefreshContactsListener {
+ private val PICK_IMPORT_SOURCE_INTENT = 1
+ private val PICK_EXPORT_FILE_INTENT = 2
+
private var isSearchOpen = false
private var searchMenuItem: MenuItem? = null
private var werePermissionsHandled = false
private var isFirstResume = true
private var isGettingContacts = false
+ private var ignoredExportContactSources = HashSet()
private var handledShowTabs = 0
private var storedTextColor = 0
@@ -157,7 +162,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
}
- val dialpadIcon = resources.getColoredDrawableWithColor(R.drawable.ic_dialpad_vector, if (isBlackAndWhiteTheme()) Color.BLACK else config.primaryColor.getContrastColor())
+ val dialpadIcon = resources.getColoredDrawableWithColor(R.drawable.ic_dialpad_vector, getFABIconColor())
main_dialpad_button.apply {
setImageDrawable(dialpadIcon)
background.applyColorFilter(getAdjustedPrimaryColor())
@@ -217,6 +222,16 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
return true
}
+ override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
+ super.onActivityResult(requestCode, resultCode, resultData)
+ if (requestCode == PICK_IMPORT_SOURCE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
+ tryImportContactsFromFile(resultData.data!!)
+ } else if (requestCode == PICK_EXPORT_FILE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
+ val outputStream = contentResolver.openOutputStream(resultData.data!!)
+ exportContactsTo(ignoredExportContactSources, outputStream)
+ }
+ }
+
private fun storeStateVariables() {
config.apply {
storedTextColor = textColor
@@ -253,12 +268,14 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
getCurrentFragment()?.onSearchOpened()
isSearchOpen = true
+ main_dialpad_button.beGone()
return true
}
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
getCurrentFragment()?.onSearchClosed()
isSearchOpen = false
+ main_dialpad_button.beVisibleIf(config.showDialpadButton)
return true
}
})
@@ -298,11 +315,11 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
val intent = Intent(this, DialpadActivity::class.java)
intent.action = Intent.ACTION_VIEW
return ShortcutInfo.Builder(this, "launch_dialpad")
- .setShortLabel(newEvent)
- .setLongLabel(newEvent)
- .setIcon(Icon.createWithBitmap(bmp))
- .setIntent(intent)
- .build()
+ .setShortLabel(newEvent)
+ .setLongLabel(newEvent)
+ .setIcon(Icon.createWithBitmap(bmp))
+ .setIntent(intent)
+ .build()
}
@SuppressLint("NewApi")
@@ -315,11 +332,11 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
val intent = Intent(this, EditContactActivity::class.java)
intent.action = Intent.ACTION_VIEW
return ShortcutInfo.Builder(this, "create_new_contact")
- .setShortLabel(newEvent)
- .setLongLabel(newEvent)
- .setIcon(Icon.createWithBitmap(bmp))
- .setIntent(intent)
- .build()
+ .setShortLabel(newEvent)
+ .setLongLabel(newEvent)
+ .setIcon(Icon.createWithBitmap(bmp))
+ .setIntent(intent)
+ .build()
}
private fun getCurrentFragment(): MyViewPagerFragment? {
@@ -383,17 +400,17 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
main_tabs_holder.onTabSelectionChanged(
- tabUnselectedAction = {
- it.icon?.applyColorFilter(config.textColor)
- },
- tabSelectedAction = {
- if (isSearchOpen) {
- getCurrentFragment()?.onSearchQueryChanged("")
- searchMenuItem?.collapseActionView()
- }
- viewpager.currentItem = it.position
- it.icon?.applyColorFilter(getAdjustedPrimaryColor())
+ tabUnselectedAction = {
+ it.icon?.applyColorFilter(config.textColor)
+ },
+ tabSelectedAction = {
+ if (isSearchOpen) {
+ getCurrentFragment()?.onSearchQueryChanged("")
+ searchMenuItem?.collapseActionView()
}
+ viewpager.currentItem = it.position
+ it.icon?.applyColorFilter(getAdjustedPrimaryColor())
+ }
)
if (intent?.action == Intent.ACTION_VIEW && intent.data != null) {
@@ -446,9 +463,17 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
private fun tryImportContacts() {
- handlePermission(PERMISSION_READ_STORAGE) {
- if (it) {
- importContacts()
+ if (isQPlus()) {
+ Intent(Intent.ACTION_GET_CONTENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "text/x-vcard"
+ startActivityForResult(this, PICK_IMPORT_SOURCE_INTENT)
+ }
+ } else {
+ handlePermission(PERMISSION_READ_STORAGE) {
+ if (it) {
+ importContacts()
+ }
}
}
}
@@ -493,28 +518,42 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
private fun tryExportContacts() {
- handlePermission(PERMISSION_WRITE_STORAGE) {
- if (it) {
- exportContacts()
+ if (isQPlus()) {
+ ExportContactsDialog(this, config.lastExportPath, true) { file, ignoredContactSources ->
+ ignoredExportContactSources = ignoredContactSources
+
+ Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
+ type = "text/x-vcard"
+ putExtra(Intent.EXTRA_TITLE, file.name)
+ addCategory(Intent.CATEGORY_OPENABLE)
+
+ startActivityForResult(this, PICK_EXPORT_FILE_INTENT)
+ }
+ }
+ } else {
+ handlePermission(PERMISSION_WRITE_STORAGE) {
+ if (it) {
+ ExportContactsDialog(this, config.lastExportPath, false) { file, ignoredContactSources ->
+ getFileOutputStream(file.toFileDirItem(this), true) {
+ exportContactsTo(ignoredContactSources, it)
+ }
+ }
+ }
}
}
}
- private fun exportContacts() {
- FilePickerDialog(this, pickFile = false, showFAB = true) {
- ExportContactsDialog(this, it) { file, ignoredContactSources ->
- ContactsHelper(this).getContacts(true, ignoredContactSources) { contacts ->
- if (contacts.isEmpty()) {
- toast(R.string.no_entries_for_exporting)
- } else {
- VcfExporter().exportContacts(this, file, contacts, true) { result ->
- toast(when (result) {
- VcfExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful
- VcfExporter.ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed
- else -> R.string.exporting_failed
- })
- }
- }
+ private fun exportContactsTo(ignoredContactSources: HashSet, outputStream: OutputStream?) {
+ ContactsHelper(this).getContacts(true, ignoredContactSources) { contacts ->
+ if (contacts.isEmpty()) {
+ toast(R.string.no_entries_for_exporting)
+ } else {
+ VcfExporter().exportContacts(this, outputStream, contacts, true) { result ->
+ toast(when (result) {
+ VcfExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful
+ VcfExporter.ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed
+ else -> R.string.exporting_failed
+ })
}
}
}
@@ -524,10 +563,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
val licenses = LICENSE_JODA or LICENSE_GLIDE or LICENSE_GSON or LICENSE_INDICATOR_FAST_SCROLL
val faqItems = arrayListOf(
- FAQItem(R.string.faq_1_title, R.string.faq_1_text),
- FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),
- FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons),
- FAQItem(R.string.faq_7_title_commons, R.string.faq_7_text_commons)
+ FAQItem(R.string.faq_1_title, R.string.faq_1_text),
+ FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),
+ FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons),
+ FAQItem(R.string.faq_7_title_commons, R.string.faq_7_text_commons)
)
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ManageBlockedNumbersActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ManageBlockedNumbersActivity.kt
deleted file mode 100644
index 7075fa15..00000000
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ManageBlockedNumbersActivity.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.simplemobiletools.contacts.pro.activities
-
-import android.content.Intent
-import android.os.Bundle
-import android.view.Menu
-import android.view.MenuItem
-import com.simplemobiletools.commons.extensions.beVisibleIf
-import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
-import com.simplemobiletools.commons.extensions.underlineText
-import com.simplemobiletools.commons.extensions.updateTextColors
-import com.simplemobiletools.commons.helpers.ensureBackgroundThread
-import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
-import com.simplemobiletools.contacts.pro.R
-import com.simplemobiletools.contacts.pro.adapters.ManageBlockedNumbersAdapter
-import com.simplemobiletools.contacts.pro.dialogs.AddBlockedNumberDialog
-import com.simplemobiletools.contacts.pro.extensions.getBlockedNumbers
-import com.simplemobiletools.contacts.pro.extensions.isDefaultDialer
-import com.simplemobiletools.contacts.pro.helpers.REQUEST_CODE_SET_DEFAULT_DIALER
-import com.simplemobiletools.contacts.pro.models.BlockedNumber
-import kotlinx.android.synthetic.main.activity_manage_blocked_numbers.*
-
-class ManageBlockedNumbersActivity : SimpleActivity(), RefreshRecyclerViewListener {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_manage_blocked_numbers)
- updateBlockedNumbers()
- updateTextColors(manage_blocked_numbers_wrapper)
- updatePlaceholderTexts()
-
- manage_blocked_numbers_placeholder_2.apply {
- underlineText()
- setTextColor(getAdjustedPrimaryColor())
- setOnClickListener {
- if (isDefaultDialer()) {
- addOrEditBlockedNumber()
- } else {
- launchSetDefaultDialerIntent()
- }
- }
- }
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.menu_add_blocked_number, menu)
- updateMenuItemColors(menu)
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- when (item.itemId) {
- R.id.add_blocked_number -> addOrEditBlockedNumber()
- else -> return super.onOptionsItemSelected(item)
- }
- return true
- }
-
- override fun refreshItems() {
- updateBlockedNumbers()
- }
-
- private fun updatePlaceholderTexts() {
- manage_blocked_numbers_placeholder.text = getString(if (isDefaultDialer()) R.string.not_blocking_anyone else R.string.must_make_default_dialer)
- manage_blocked_numbers_placeholder_2.text = getString(if (isDefaultDialer()) R.string.add_a_blocked_number else R.string.set_as_default)
- }
-
- private fun updateBlockedNumbers() {
- ensureBackgroundThread {
- val blockedNumbers = getBlockedNumbers()
- runOnUiThread {
- ManageBlockedNumbersAdapter(this, blockedNumbers, this, manage_blocked_numbers_list) {
- addOrEditBlockedNumber(it as BlockedNumber)
- }.apply {
- manage_blocked_numbers_list.adapter = this
- }
-
- manage_blocked_numbers_placeholder.beVisibleIf(blockedNumbers.isEmpty())
- manage_blocked_numbers_placeholder_2.beVisibleIf(blockedNumbers.isEmpty())
- }
- }
- }
-
- private fun addOrEditBlockedNumber(currentNumber: BlockedNumber? = null) {
- AddBlockedNumberDialog(this, currentNumber) {
- updateBlockedNumbers()
- }
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
- super.onActivityResult(requestCode, resultCode, resultData)
- if (requestCode == REQUEST_CODE_SET_DEFAULT_DIALER && isDefaultDialer()) {
- updatePlaceholderTexts()
- updateBlockedNumbers()
- }
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SelectContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SelectContactActivity.kt
index af80e499..1b680541 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SelectContactActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SelectContactActivity.kt
@@ -6,6 +6,8 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
+import android.provider.ContactsContract.CommonDataKinds.Email
+import android.provider.ContactsContract.CommonDataKinds.Phone
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
@@ -46,8 +48,8 @@ class SelectContactActivity : SimpleActivity() {
handlePermission(PERMISSION_WRITE_CONTACTS) {
if (it) {
specialMimeType = when (intent.data) {
- ContactsContract.CommonDataKinds.Email.CONTENT_URI -> ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI -> ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+ Email.CONTENT_URI -> Email.CONTENT_ITEM_TYPE
+ Phone.CONTENT_URI -> Phone.CONTENT_ITEM_TYPE
else -> null
}
initContacts()
@@ -178,8 +180,8 @@ class SelectContactActivity : SimpleActivity() {
var contacts = it.filter {
if (specialMimeType != null) {
val hasRequiredValues = when (specialMimeType) {
- ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> it.emails.isNotEmpty()
- ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> it.phoneNumbers.isNotEmpty()
+ Email.CONTENT_ITEM_TYPE -> it.emails.isNotEmpty()
+ Phone.CONTENT_ITEM_TYPE -> it.phoneNumbers.isNotEmpty()
else -> true
}
!it.isPrivate() && hasRequiredValues
@@ -242,8 +244,8 @@ class SelectContactActivity : SimpleActivity() {
select_contact_placeholder_2.beVisibleIf(contacts.isEmpty())
select_contact_placeholder.beVisibleIf(contacts.isEmpty())
select_contact_placeholder.setText(when (specialMimeType) {
- ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> R.string.no_contacts_with_emails
- ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> R.string.no_contacts_with_phone_numbers
+ Email.CONTENT_ITEM_TYPE -> R.string.no_contacts_with_emails
+ Phone.CONTENT_ITEM_TYPE -> R.string.no_contacts_with_phone_numbers
else -> R.string.no_contacts_found
})
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt
index ba48de77..b1764815 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt
@@ -5,6 +5,7 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.Menu
+import com.simplemobiletools.commons.activities.ManageBlockedNumbersActivity
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.getFontSizeText
@@ -91,10 +92,10 @@ class SettingsActivity : SimpleActivity() {
settings_font_size.text = getFontSizeText()
settings_font_size_holder.setOnClickListener {
val items = arrayListOf(
- RadioItem(FONT_SIZE_SMALL, getString(R.string.small)),
- RadioItem(FONT_SIZE_MEDIUM, getString(R.string.medium)),
- RadioItem(FONT_SIZE_LARGE, getString(R.string.large)),
- RadioItem(FONT_SIZE_EXTRA_LARGE, getString(R.string.extra_large)))
+ RadioItem(FONT_SIZE_SMALL, getString(R.string.small)),
+ RadioItem(FONT_SIZE_MEDIUM, getString(R.string.medium)),
+ RadioItem(FONT_SIZE_LARGE, getString(R.string.large)),
+ RadioItem(FONT_SIZE_EXTRA_LARGE, getString(R.string.extra_large)))
RadioGroupDialog(this@SettingsActivity, items, config.fontSize) {
config.fontSize = it as Int
@@ -165,9 +166,9 @@ class SettingsActivity : SimpleActivity() {
settings_on_contact_click.text = getOnContactClickText()
settings_on_contact_click_holder.setOnClickListener {
val items = arrayListOf(
- RadioItem(ON_CLICK_CALL_CONTACT, getString(R.string.call_contact)),
- RadioItem(ON_CLICK_VIEW_CONTACT, getString(R.string.view_contact)),
- RadioItem(ON_CLICK_EDIT_CONTACT, getString(R.string.edit_contact)))
+ RadioItem(ON_CLICK_CALL_CONTACT, getString(R.string.call_contact)),
+ RadioItem(ON_CLICK_VIEW_CONTACT, getString(R.string.view_contact)),
+ RadioItem(ON_CLICK_EDIT_CONTACT, getString(R.string.edit_contact)))
RadioGroupDialog(this@SettingsActivity, items, config.onContactClick) {
config.onContactClick = it as Int
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SimpleActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SimpleActivity.kt
index 4b3538c4..54fed533 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SimpleActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SimpleActivity.kt
@@ -1,40 +1,39 @@
package com.simplemobiletools.contacts.pro.activities
-import android.annotation.TargetApi
import android.content.ContentValues
import android.content.Intent
import android.graphics.drawable.Drawable
import android.net.Uri
-import android.os.Build
-import android.telecom.TelecomManager
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.getColoredDrawableWithColor
-import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.extensions.config
-import com.simplemobiletools.contacts.pro.helpers.*
+import com.simplemobiletools.contacts.pro.helpers.KEY_MAILTO
+import com.simplemobiletools.contacts.pro.helpers.KEY_PHONE
+import com.simplemobiletools.contacts.pro.helpers.LOCATION_CONTACTS_TAB
+import com.simplemobiletools.contacts.pro.helpers.LOCATION_FAVORITES_TAB
open class SimpleActivity : BaseSimpleActivity() {
override fun getAppIconIDs() = arrayListOf(
- R.mipmap.ic_launcher_red,
- R.mipmap.ic_launcher_pink,
- R.mipmap.ic_launcher_purple,
- R.mipmap.ic_launcher_deep_purple,
- R.mipmap.ic_launcher_indigo,
- R.mipmap.ic_launcher_blue,
- R.mipmap.ic_launcher_light_blue,
- R.mipmap.ic_launcher_cyan,
- R.mipmap.ic_launcher_teal,
- R.mipmap.ic_launcher_green,
- R.mipmap.ic_launcher_light_green,
- R.mipmap.ic_launcher_lime,
- R.mipmap.ic_launcher_yellow,
- R.mipmap.ic_launcher_amber,
- R.mipmap.ic_launcher,
- R.mipmap.ic_launcher_deep_orange,
- R.mipmap.ic_launcher_brown,
- R.mipmap.ic_launcher_blue_grey,
- R.mipmap.ic_launcher_grey_black
+ R.mipmap.ic_launcher_red,
+ R.mipmap.ic_launcher_pink,
+ R.mipmap.ic_launcher_purple,
+ R.mipmap.ic_launcher_deep_purple,
+ R.mipmap.ic_launcher_indigo,
+ R.mipmap.ic_launcher_blue,
+ R.mipmap.ic_launcher_light_blue,
+ R.mipmap.ic_launcher_cyan,
+ R.mipmap.ic_launcher_teal,
+ R.mipmap.ic_launcher_green,
+ R.mipmap.ic_launcher_light_green,
+ R.mipmap.ic_launcher_lime,
+ R.mipmap.ic_launcher_yellow,
+ R.mipmap.ic_launcher_amber,
+ R.mipmap.ic_launcher,
+ R.mipmap.ic_launcher_deep_orange,
+ R.mipmap.ic_launcher_brown,
+ R.mipmap.ic_launcher_blue_grey,
+ R.mipmap.ic_launcher_grey_black
)
override fun getAppLauncherName() = getString(R.string.app_launcher_name)
@@ -64,22 +63,11 @@ open class SimpleActivity : BaseSimpleActivity() {
}
}
- @TargetApi(Build.VERSION_CODES.M)
- protected fun launchSetDefaultDialerIntent() {
- Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER).putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName).apply {
- if (resolveActivity(packageManager) != null) {
- startActivityForResult(this, REQUEST_CODE_SET_DEFAULT_DIALER)
- } else {
- toast(R.string.no_app_found)
- }
- }
- }
-
protected fun getTabIcon(position: Int): Drawable {
val drawableId = when (position) {
LOCATION_CONTACTS_TAB -> R.drawable.ic_person_vector
LOCATION_FAVORITES_TAB -> R.drawable.ic_star_on_vector
- else -> R.drawable.ic_group_vector
+ else -> R.drawable.ic_people_vector
}
return resources.getColoredDrawableWithColor(drawableId, config.textColor)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt
index 89f34622..73dd99bf 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt
@@ -9,6 +9,9 @@ import android.view.View
import android.view.WindowManager
import android.widget.RelativeLayout
import com.bumptech.glide.Glide
+import com.bumptech.glide.load.resource.bitmap.FitCenter
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.RequestOptions
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
@@ -70,6 +73,14 @@ class ViewContactActivity : ContactActivity() {
}
}
+ override fun onBackPressed() {
+ if (contact_photo_big.alpha == 1f) {
+ hideBigContactPhoto()
+ } else {
+ super.onBackPressed()
+ }
+ }
+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_view_contact, menu)
menu.apply {
@@ -155,15 +166,18 @@ class ViewContactActivity : ContactActivity() {
contact_start_call.beVisibleIf(contact!!.phoneNumbers.isNotEmpty())
contact_send_email.beVisibleIf(contact!!.emails.isNotEmpty())
- val background = resources.getDrawable(R.drawable.contact_circular_background)
- background.applyColorFilter(config.primaryColor)
- contact_photo.background = background
-
if (contact!!.photoUri.isEmpty() && contact!!.photo == null) {
showPhotoPlaceholder(contact_photo)
} else {
updateContactPhoto(contact!!.photoUri, contact_photo, contact!!.photo)
- Glide.with(this).load(contact!!.photo ?: currentContactPhotoPath).into(contact_photo_big)
+ val options = RequestOptions()
+ .transform(FitCenter(), RoundedCorners(resources.getDimension(R.dimen.normal_margin).toInt()))
+
+ Glide.with(this)
+ .load(contact!!.photo ?: currentContactPhotoPath)
+ .apply(options)
+ .into(contact_photo_big)
+
contact_photo.setOnClickListener {
contact_photo_big.alpha = 0f
contact_photo_big.beVisible()
@@ -171,24 +185,16 @@ class ViewContactActivity : ContactActivity() {
}
contact_photo_big.setOnClickListener {
- contact_photo_big.animate().alpha(0f).withEndAction { it.beGone() }.start()
+ hideBigContactPhoto()
}
}
val textColor = config.textColor
- contact_send_sms.applyColorFilter(textColor)
- contact_start_call.applyColorFilter(textColor)
- contact_send_email.applyColorFilter(textColor)
- contact_name_image.applyColorFilter(textColor)
- contact_numbers_image.applyColorFilter(textColor)
- contact_emails_image.applyColorFilter(textColor)
- contact_addresses_image.applyColorFilter(textColor)
- contact_events_image.applyColorFilter(textColor)
- contact_source_image.applyColorFilter(textColor)
- contact_notes_image.applyColorFilter(textColor)
- contact_organization_image.applyColorFilter(textColor)
- contact_websites_image.applyColorFilter(textColor)
- contact_groups_image.applyColorFilter(textColor)
+ arrayOf(contact_send_sms, contact_start_call, contact_send_email, 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 {
+ it.applyColorFilter(textColor)
+ }
contact_send_sms.setOnClickListener { trySendSMS() }
contact_start_call.setOnClickListener { tryStartCall(contact!!) }
@@ -288,7 +294,7 @@ class ViewContactActivity : ContactActivity() {
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_nickname.isGone()) {
contact_name_image.beInvisible()
(contact_photo.layoutParams as RelativeLayout.LayoutParams).bottomMargin = resources.getDimension(R.dimen.medium_margin).toInt()
}
@@ -617,6 +623,10 @@ class ViewContactActivity : ContactActivity() {
private fun getStarDrawable(on: Boolean) = resources.getDrawable(if (on) R.drawable.ic_star_on_vector else R.drawable.ic_star_off_vector)
+ private fun hideBigContactPhoto() {
+ contact_photo_big.animate().alpha(0f).withEndAction { contact_photo_big.beGone() }.start()
+ }
+
private fun View.copyOnLongClick(value: String) {
setOnLongClickListener {
copyToClipboard(value)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt
index f3d9678b..7d44abcc 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt
@@ -1,13 +1,12 @@
package com.simplemobiletools.contacts.pro.adapters
-import android.graphics.drawable.Drawable
+import android.graphics.drawable.BitmapDrawable
import android.util.TypedValue
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.signature.ObjectKey
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
@@ -35,8 +34,6 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList, highlightText: String = "") {
if (newItems.hashCode() != contactItems.hashCode()) {
contactItems = newItems.clone() as ArrayList
@@ -154,7 +141,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList {
- val options = RequestOptions()
- .signature(ObjectKey(contact.photoUri))
- .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
- .error(placeholderImage)
- .centerCrop()
+ val placeholderImage = BitmapDrawable(resources, context.getContactLetterIcon(fullName))
+ if (contact.photoUri.isEmpty() && contact.photo == null) {
+ contact_tmb.setImageDrawable(placeholderImage)
+ } else {
+ val options = RequestOptions()
+ .signature(ObjectKey(contact.getSignatureKey()))
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
+ .error(placeholderImage)
+ .centerCrop()
- Glide.with(activity)
- .load(contact.photoUri)
- .transition(DrawableTransitionOptions.withCrossFade())
- .apply(options)
- .apply(RequestOptions.circleCropTransform())
- .into(contact_tmb)
- contact_tmb.setPadding(smallPadding, smallPadding, smallPadding, smallPadding)
+ val itemToLoad: Any? = if (contact.photoUri.isNotEmpty()) {
+ contact.photoUri
+ } else {
+ contact.photo
}
- contact.photo != null -> {
- val options = RequestOptions()
- .signature(ObjectKey(contact.hashCode()))
- .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
- .error(placeholderImage)
- .centerCrop()
- Glide.with(activity)
- .load(contact.photo)
- .transition(DrawableTransitionOptions.withCrossFade())
- .apply(options)
- .apply(RequestOptions.circleCropTransform())
- .into(contact_tmb)
- contact_tmb.setPadding(smallPadding, smallPadding, smallPadding, smallPadding)
- }
- else -> {
- contact_tmb.setPadding(mediumPadding, mediumPadding, mediumPadding, mediumPadding)
- contact_tmb.setImageDrawable(placeholderImage)
- }
+ Glide.with(activity)
+ .load(itemToLoad)
+ .apply(options)
+ .apply(RequestOptions.circleCropTransform())
+ .into(contact_tmb)
}
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt
index cd3eeb82..9b9c0235 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt
@@ -25,8 +25,6 @@ import java.util.*
class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList, val refreshListener: RefreshContactsListener?, recyclerView: MyRecyclerView,
fastScroller: FastScroller, itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
- private var smallPadding = activity.resources.getDimension(R.dimen.small_margin).toInt()
- private var bigPadding = activity.resources.getDimension(R.dimen.normal_margin).toInt()
private var textToHighlight = ""
var adjustedPrimaryColor = activity.getAdjustedPrimaryColor()
@@ -113,7 +111,7 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList, val
resources.getQuantityString(R.plurals.delete_groups, itemsCnt, itemsCnt)
}
- val baseString = R.string.delete_contacts_confirmation
+ val baseString = R.string.deletion_confirmation
val question = String.format(resources.getString(baseString), items)
ConfirmationDialog(activity, question) {
@@ -165,17 +163,11 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList, val
setTextColor(textColor)
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
text = groupTitle
-
- if (showContactThumbnails) {
- setPadding(smallPadding, bigPadding, bigPadding, bigPadding)
- } else {
- setPadding(bigPadding, bigPadding, bigPadding, bigPadding)
- }
}
group_tmb.beVisibleIf(showContactThumbnails)
if (showContactThumbnails) {
- group_tmb.applyColorFilter(textColor)
+ group_tmb.setImageDrawable(activity.getColoredGroupIcon(group.title))
}
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ManageBlockedNumbersAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ManageBlockedNumbersAdapter.kt
deleted file mode 100644
index cdf55829..00000000
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ManageBlockedNumbersAdapter.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.simplemobiletools.contacts.pro.adapters
-
-import android.view.Menu
-import android.view.View
-import android.view.ViewGroup
-import com.simplemobiletools.commons.activities.BaseSimpleActivity
-import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
-import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
-import com.simplemobiletools.commons.views.MyRecyclerView
-import com.simplemobiletools.contacts.pro.R
-import com.simplemobiletools.contacts.pro.extensions.config
-import com.simplemobiletools.contacts.pro.extensions.deleteBlockedNumber
-import com.simplemobiletools.contacts.pro.models.BlockedNumber
-import kotlinx.android.synthetic.main.item_manage_blocked_number.view.*
-import java.util.*
-
-class ManageBlockedNumbersAdapter(activity: BaseSimpleActivity, var blockedNumbers: ArrayList, val listener: RefreshRecyclerViewListener?,
- recyclerView: MyRecyclerView, itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, null, itemClick) {
-
- private val config = activity.config
-
- init {
- setupDragListener(true)
- }
-
- override fun getActionMenuId() = R.menu.cab_remove_only
-
- override fun prepareActionMode(menu: Menu) {}
-
- override fun actionItemPressed(id: Int) {
- when (id) {
- R.id.cab_remove -> removeSelection()
- }
- }
-
- override fun getSelectableItemCount() = blockedNumbers.size
-
- override fun getIsItemSelectable(position: Int) = true
-
- override fun getItemSelectionKey(position: Int) = blockedNumbers.getOrNull(position)?.id?.toInt()
-
- override fun getItemKeyPosition(key: Int) = blockedNumbers.indexOfFirst { it.id.toInt() == key }
-
- override fun onActionModeCreated() {}
-
- override fun onActionModeDestroyed() {}
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_blocked_number, parent)
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- val blockedNumber = blockedNumbers[position]
- holder.bindView(blockedNumber, true, true) { itemView, adapterPosition ->
- setupView(itemView, blockedNumber)
- }
- bindViewHolder(holder)
- }
-
- override fun getItemCount() = blockedNumbers.size
-
- private fun getSelectedItems() = blockedNumbers.filter { selectedKeys.contains(it.id.toInt()) } as ArrayList
-
- private fun setupView(view: View, blockedNumber: BlockedNumber) {
- view.apply {
- manage_blocked_number_holder?.isSelected = selectedKeys.contains(blockedNumber.id.toInt())
- manage_blocked_number_title.apply {
- text = blockedNumber.number
- setTextColor(config.textColor)
- }
- }
- }
-
- private fun removeSelection() {
- val removeBlockedNumbers = ArrayList(selectedKeys.size)
- val positions = getSelectedItemPositions()
-
- getSelectedItems().forEach {
- removeBlockedNumbers.add(it)
- activity.deleteBlockedNumber(it.number)
- }
-
- blockedNumbers.removeAll(removeBlockedNumbers)
- removeSelectedItems(positions)
- if (blockedNumbers.isEmpty()) {
- listener?.refreshItems()
- }
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt
index e69b25fe..65e2025e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt
@@ -1,5 +1,6 @@
package com.simplemobiletools.contacts.pro.adapters
+import android.graphics.drawable.BitmapDrawable
import android.util.SparseArray
import android.util.TypedValue
import android.view.View
@@ -7,7 +8,6 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.signature.ObjectKey
import com.simplemobiletools.commons.extensions.*
@@ -27,20 +27,14 @@ class SelectContactsAdapter(val activity: SimpleActivity, var contacts: ArrayLis
private val itemViews = SparseArray()
private val selectedPositions = HashSet()
private val config = activity.config
- private val textColor = config.textColor
private val adjustedPrimaryColor = activity.getAdjustedPrimaryColor()
private val fontSize = activity.getTextSize()
- private val contactDrawable = activity.resources.getColoredDrawableWithColor(R.drawable.ic_person_vector, textColor)
private val showContactThumbnails = config.showContactThumbnails
private val showPhoneNumbers = config.showPhoneNumbers
private val itemLayout = if (showPhoneNumbers) R.layout.item_add_favorite_with_number else R.layout.item_add_favorite_without_number
private var textToHighlight = ""
- private var smallPadding = activity.resources.getDimension(R.dimen.small_margin).toInt()
- private var mediumPadding = activity.resources.getDimension(R.dimen.medium_margin).toInt()
- private var bigPadding = activity.resources.getDimension(R.dimen.normal_margin).toInt()
-
init {
contacts.forEachIndexed { index, contact ->
if (selectedContacts.asSequence().map { it.id }.contains(contact.id)) {
@@ -121,11 +115,6 @@ class SelectContactsAdapter(val activity: SimpleActivity, var contacts: ArrayLis
contact_name.setTextColor(textColor)
contact_name.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
- if (!showContactThumbnails && !showPhoneNumbers) {
- contact_name.setPadding(bigPadding, bigPadding, bigPadding, bigPadding)
- } else {
- contact_name.setPadding(if (showContactThumbnails) smallPadding else bigPadding, smallPadding, smallPadding, 0)
- }
if (contact_number != null) {
val phoneNumberToUse = if (textToHighlight.isEmpty()) {
@@ -137,7 +126,6 @@ class SelectContactsAdapter(val activity: SimpleActivity, var contacts: ArrayLis
val numberText = phoneNumberToUse?.value ?: ""
contact_number.text = if (textToHighlight.isEmpty()) numberText else numberText.highlightTextPart(textToHighlight, adjustedPrimaryColor, false, true)
contact_number.setTextColor(textColor)
- contact_number.setPadding(if (showContactThumbnails) smallPadding else bigPadding, 0, smallPadding, 0)
contact_number.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
}
@@ -150,40 +138,36 @@ class SelectContactsAdapter(val activity: SimpleActivity, var contacts: ArrayLis
}
contact_tmb.beVisibleIf(showContactThumbnails)
- if (showContactThumbnails) {
- if (contact.photoUri.isNotEmpty()) {
- val options = RequestOptions()
- .signature(ObjectKey(contact.photoUri))
- .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
- .error(contactDrawable)
- .centerCrop()
- if (!activity.isDestroyed && !activity.isFinishing) {
- Glide.with(activity)
- .load(contact.photoUri)
- .transition(DrawableTransitionOptions.withCrossFade())
- .apply(options)
- .apply(RequestOptions.circleCropTransform())
- .into(contact_tmb)
- contact_tmb.setPadding(smallPadding, smallPadding, smallPadding, smallPadding)
- }
- } else if (contact.photo != null) {
+ if (showContactThumbnails) {
+ val avatarName = when {
+ contact.isABusinessContact() -> contact.getFullCompany()
+ config.startNameWithSurname -> contact.surname
+ else -> contact.firstName
+ }
+
+ val placeholderImage = BitmapDrawable(resources, context.getContactLetterIcon(avatarName))
+
+ if (contact.photoUri.isEmpty() && contact.photo == null) {
+ contact_tmb.setImageDrawable(placeholderImage)
+ } else {
val options = RequestOptions()
- .signature(ObjectKey(contact.hashCode()))
- .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
- .error(contactDrawable)
- .centerCrop()
+ .signature(ObjectKey(contact.getSignatureKey()))
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
+ .error(placeholderImage)
+ .centerCrop()
+
+ val itemToLoad: Any? = if (contact.photoUri.isNotEmpty()) {
+ contact.photoUri
+ } else {
+ contact.photo
+ }
Glide.with(activity)
- .load(contact.photo)
- .transition(DrawableTransitionOptions.withCrossFade())
- .apply(options)
- .apply(RequestOptions.circleCropTransform())
- .into(contact_tmb)
- contact_tmb.setPadding(smallPadding, smallPadding, smallPadding, smallPadding)
- } else {
- contact_tmb.setPadding(mediumPadding, mediumPadding, mediumPadding, mediumPadding)
- contact_tmb.setImageDrawable(contactDrawable)
+ .load(itemToLoad)
+ .apply(options)
+ .apply(RequestOptions.circleCropTransform())
+ .into(contact_tmb)
}
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/databases/ContactsDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/databases/ContactsDatabase.kt
index 530b1cde..042ab5c0 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/databases/ContactsDatabase.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/databases/ContactsDatabase.kt
@@ -5,6 +5,7 @@ import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
+import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.simplemobiletools.contacts.pro.helpers.Converters
import com.simplemobiletools.contacts.pro.helpers.FIRST_CONTACT_ID
@@ -16,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 = 1)
+@Database(entities = [LocalContact::class, Group::class], version = 2)
@TypeConverters(Converters::class)
abstract class ContactsDatabase : RoomDatabase() {
@@ -32,13 +33,14 @@ abstract class ContactsDatabase : RoomDatabase() {
synchronized(ContactsDatabase::class) {
if (db == null) {
db = Room.databaseBuilder(context.applicationContext, ContactsDatabase::class.java, "local_contacts.db")
- .addCallback(object : Callback() {
- override fun onCreate(db: SupportSQLiteDatabase) {
- super.onCreate(db)
- increaseAutoIncrementIds()
- }
- })
- .build()
+ .addCallback(object : Callback() {
+ override fun onCreate(db: SupportSQLiteDatabase) {
+ super.onCreate(db)
+ increaseAutoIncrementIds()
+ }
+ })
+ .addMigrations(MIGRATION_1_2)
+ .build()
}
}
}
@@ -67,5 +69,13 @@ abstract class ContactsDatabase : RoomDatabase() {
}
}
}
+
+ private val MIGRATION_1_2 = object : Migration(1, 2) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.apply {
+ execSQL("ALTER TABLE contacts ADD COLUMN photo_uri TEXT NOT NULL DEFAULT ''")
+ }
+ }
+ }
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/AddBlockedNumberDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/AddBlockedNumberDialog.kt
deleted file mode 100644
index 32a9497f..00000000
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/AddBlockedNumberDialog.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.simplemobiletools.contacts.pro.dialogs
-
-import androidx.appcompat.app.AlertDialog
-import com.simplemobiletools.commons.activities.BaseSimpleActivity
-import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.commons.extensions.showKeyboard
-import com.simplemobiletools.commons.extensions.value
-import com.simplemobiletools.contacts.pro.R
-import com.simplemobiletools.contacts.pro.extensions.addBlockedNumber
-import com.simplemobiletools.contacts.pro.extensions.deleteBlockedNumber
-import com.simplemobiletools.contacts.pro.models.BlockedNumber
-import kotlinx.android.synthetic.main.dialog_add_blocked_number.view.*
-
-class AddBlockedNumberDialog(val activity: BaseSimpleActivity, val originalNumber: BlockedNumber? = null, val callback: () -> Unit) {
- init {
- val view = activity.layoutInflater.inflate(R.layout.dialog_add_blocked_number, null).apply {
- if (originalNumber != null) {
- add_blocked_number_edittext.setText(originalNumber.number)
- }
- }
-
- AlertDialog.Builder(activity)
- .setPositiveButton(R.string.ok, null)
- .setNegativeButton(R.string.cancel, null)
- .create().apply {
- activity.setupDialogStuff(view, this) {
- showKeyboard(view.add_blocked_number_edittext)
- getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
- val newBlockedNumber = view.add_blocked_number_edittext.value
- if (originalNumber != null && newBlockedNumber != originalNumber.number) {
- activity.deleteBlockedNumber(originalNumber.number)
- }
-
- if (newBlockedNumber.isNotEmpty()) {
- activity.addBlockedNumber(newBlockedNumber)
- }
-
- callback()
- dismiss()
- }
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt
index 7939b743..f0457a18 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt
@@ -2,11 +2,13 @@ package com.simplemobiletools.contacts.pro.dialogs
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
+import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.activities.SimpleActivity
import com.simplemobiletools.contacts.pro.adapters.FilterContactSourcesAdapter
+import com.simplemobiletools.contacts.pro.extensions.config
import com.simplemobiletools.contacts.pro.extensions.getVisibleContactSources
import com.simplemobiletools.contacts.pro.helpers.ContactsHelper
import com.simplemobiletools.contacts.pro.models.ContactSource
@@ -14,15 +16,30 @@ import kotlinx.android.synthetic.main.dialog_export_contacts.view.*
import java.io.File
import java.util.*
-class ExportContactsDialog(val activity: SimpleActivity, val path: String, private val callback: (file: File, ignoredContactSources: HashSet) -> Unit) {
+class ExportContactsDialog(val activity: SimpleActivity, val path: String, val hidePath: Boolean,
+ private val callback: (file: File, ignoredContactSources: HashSet) -> Unit) {
private var contactSources = ArrayList()
private var ignoreClicks = false
+ private var realPath = if (path.isEmpty()) activity.internalStoragePath else path
init {
val view = (activity.layoutInflater.inflate(R.layout.dialog_export_contacts, null) as ViewGroup).apply {
- export_contacts_folder.text = activity.humanizePath(path)
+ export_contacts_folder.text = activity.humanizePath(realPath)
export_contacts_filename.setText("contacts_${activity.getCurrentFormattedDateTime()}")
+ if (hidePath) {
+ export_contacts_folder_label.beGone()
+ export_contacts_folder.beGone()
+ } else {
+ export_contacts_folder.setOnClickListener {
+ activity.hideKeyboard(export_contacts_filename)
+ FilePickerDialog(activity, realPath, false, showFAB = true) {
+ export_contacts_folder.text = activity.humanizePath(it)
+ realPath = it
+ }
+ }
+ }
+
ContactsHelper(activity).getContactSources {
it.mapTo(contactSources) { it.copy() }
activity.runOnUiThread {
@@ -45,14 +62,15 @@ class ExportContactsDialog(val activity: SimpleActivity, val path: String, priva
when {
filename.isEmpty() -> activity.toast(R.string.empty_name)
filename.isAValidFilename() -> {
- val file = File(path, "$filename.vcf")
- if (file.exists()) {
+ val file = File(realPath, "$filename.vcf")
+ if (!hidePath && file.exists()) {
activity.toast(R.string.name_taken)
return@setOnClickListener
}
ignoreClicks = true
ensureBackgroundThread {
+ activity.config.lastExportPath = file.absolutePath.getParentPath()
val selectedSources = (view.export_contacts_list.adapter as FilterContactSourcesAdapter).getSelectedContactSources()
val ignoredSources = contactSources.filter { !selectedSources.contains(it) }.map { it.getFullIdentifier() }.toHashSet()
callback(file, ignoredSources)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt
index 9995e35c..19d4ef1e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt
@@ -4,9 +4,7 @@ import android.content.Intent
import android.net.Uri
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
-import com.simplemobiletools.commons.extensions.sharePathIntent
-import com.simplemobiletools.commons.extensions.showErrorToast
-import com.simplemobiletools.commons.extensions.toast
+import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.pro.BuildConfig
@@ -57,20 +55,13 @@ fun SimpleActivity.startCall(contact: Contact) {
}
fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (newSource: String) -> Unit) {
- ContactsHelper(this).getContactSources {
- val ignoredTypes = arrayListOf(
- SIGNAL_PACKAGE,
- TELEGRAM_PACKAGE,
- WHATSAPP_PACKAGE
- )
-
+ ContactsHelper(this).getSaveableContactSources { sources ->
val items = ArrayList()
- val filteredSources = it.filter { !ignoredTypes.contains(it.type) }
- var sources = filteredSources.map { it.name }
- var currentSourceIndex = sources.indexOfFirst { it == currentSource }
- sources = filteredSources.map { it.publicName }
+ var sourceNames = sources.map { it.name }
+ var currentSourceIndex = sourceNames.indexOfFirst { it == currentSource }
+ sourceNames = sources.map { it.publicName }
- sources.forEachIndexed { index, account ->
+ sourceNames.forEachIndexed { index, account ->
items.add(RadioItem(index, account))
if (currentSource == SMT_PRIVATE && account == getString(R.string.phone_storage_hidden)) {
currentSourceIndex = index
@@ -79,7 +70,7 @@ fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (new
runOnUiThread {
RadioGroupDialog(this, items, currentSourceIndex) {
- callback(filteredSources[it as Int].name)
+ callback(sources[it as Int].name)
}
}
}
@@ -92,11 +83,13 @@ fun BaseSimpleActivity.shareContacts(contacts: ArrayList) {
return
}
- VcfExporter().exportContacts(this, file, contacts, false) {
- if (it == VcfExporter.ExportResult.EXPORT_OK) {
- sharePathIntent(file.absolutePath, BuildConfig.APPLICATION_ID)
- } else {
- showErrorToast("$it")
+ getFileOutputStream(file.toFileDirItem(this), true) {
+ VcfExporter().exportContacts(this, it, contacts, false) {
+ if (it == VcfExporter.ExportResult.EXPORT_OK) {
+ sharePathIntent(file.absolutePath, BuildConfig.APPLICATION_ID)
+ } else {
+ showErrorToast("$it")
+ }
}
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt
index c7072406..e9cafbc2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt
@@ -1,24 +1,20 @@
package com.simplemobiletools.contacts.pro.extensions
-import android.annotation.TargetApi
-import android.content.ContentValues
import android.content.Context
+import android.content.Context.AUDIO_SERVICE
import android.content.Intent
import android.database.Cursor
+import android.media.AudioManager
import android.net.Uri
-import android.os.Build
import android.os.Handler
import android.os.Looper
-import android.provider.BlockedNumberContract
-import android.provider.BlockedNumberContract.BlockedNumbers
import android.provider.ContactsContract
-import android.telecom.TelecomManager
import androidx.core.content.FileProvider
-import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.extensions.getIntValue
+import com.simplemobiletools.commons.extensions.hasPermission
+import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CONTACTS
-import com.simplemobiletools.commons.helpers.isMarshmallowPlus
-import com.simplemobiletools.commons.helpers.isNougatPlus
import com.simplemobiletools.contacts.pro.BuildConfig
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.activities.EditContactActivity
@@ -27,7 +23,6 @@ import com.simplemobiletools.contacts.pro.databases.ContactsDatabase
import com.simplemobiletools.contacts.pro.helpers.*
import com.simplemobiletools.contacts.pro.interfaces.ContactsDao
import com.simplemobiletools.contacts.pro.interfaces.GroupsDao
-import com.simplemobiletools.contacts.pro.models.BlockedNumber
import com.simplemobiletools.contacts.pro.models.Contact
import com.simplemobiletools.contacts.pro.models.ContactSource
import com.simplemobiletools.contacts.pro.models.Organization
@@ -39,13 +34,13 @@ val Context.contactsDB: ContactsDao get() = ContactsDatabase.getInstance(applica
val Context.groupsDB: GroupsDao get() = ContactsDatabase.getInstance(applicationContext).GroupsDao()
-val Context.telecomManager: TelecomManager get() = getSystemService(Context.TELECOM_SERVICE) as TelecomManager
+val Context.audioManager: AudioManager get() = getSystemService(AUDIO_SERVICE) as AudioManager
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())
}
fun Context.viewContact(contact: Contact) {
@@ -235,7 +230,7 @@ fun Context.sendSMSToContacts(contacts: ArrayList) {
val numbers = StringBuilder()
contacts.forEach {
val number = it.phoneNumbers.firstOrNull { it.type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE }
- ?: it.phoneNumbers.firstOrNull()
+ ?: it.phoneNumbers.firstOrNull()
if (number != null) {
numbers.append("${number.value};")
}
@@ -321,7 +316,7 @@ fun Context.getVisibleContactSources(): ArrayList {
val sources = getAllContactSources()
val ignoredContactSources = config.ignoredContactSources
return ArrayList(sources).filter { !ignoredContactSources.contains(it.getFullIdentifier()) }
- .map { it.name }.toMutableList() as ArrayList
+ .map { it.name }.toMutableList() as ArrayList
}
fun Context.getAllContactSources(): ArrayList {
@@ -330,60 +325,4 @@ fun Context.getAllContactSources(): ArrayList {
return sources.toMutableList() as ArrayList
}
-@TargetApi(Build.VERSION_CODES.N)
-fun Context.getBlockedNumbers(): ArrayList {
- val blockedNumbers = ArrayList()
- if (!isNougatPlus() || !isDefaultDialer()) {
- return blockedNumbers
- }
-
- val uri = BlockedNumberContract.BlockedNumbers.CONTENT_URI
- val projection = arrayOf(
- BlockedNumberContract.BlockedNumbers.COLUMN_ID,
- BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
- BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER
- )
-
- var cursor: Cursor? = null
- try {
- cursor = contentResolver.query(uri, projection, null, null, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getLongValue(BlockedNumberContract.BlockedNumbers.COLUMN_ID)
- val number = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER) ?: ""
- val normalizedNumber = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER) ?: ""
- val blockedNumber = BlockedNumber(id, number, normalizedNumber)
- blockedNumbers.add(blockedNumber)
- } while (cursor.moveToNext())
- }
- } finally {
- cursor?.close()
- }
-
- return blockedNumbers
-}
-
-@TargetApi(Build.VERSION_CODES.N)
-fun Context.addBlockedNumber(number: String) {
- ContentValues().apply {
- put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, number)
- try {
- contentResolver.insert(BlockedNumbers.CONTENT_URI, this)
- } catch (e: Exception) {
- showErrorToast(e)
- }
- }
-}
-
-@TargetApi(Build.VERSION_CODES.N)
-fun Context.deleteBlockedNumber(number: String) {
- val values = ContentValues()
- values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, number)
- val uri = contentResolver.insert(BlockedNumbers.CONTENT_URI, values)
- contentResolver.delete(uri!!, null, null)
-}
-
-@TargetApi(Build.VERSION_CODES.M)
-fun Context.isDefaultDialer() = isMarshmallowPlus() && telecomManager.defaultDialerPackage == packageName
-
fun Context.getPrivateContactSource() = ContactSource(SMT_PRIVATE, SMT_PRIVATE, getString(R.string.phone_storage_hidden))
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt
index 6b20b68d..6bfca4a4 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt
@@ -2,7 +2,6 @@ package com.simplemobiletools.contacts.pro.fragments
import android.content.Context
import android.content.Intent
-import android.content.res.ColorStateList
import android.util.AttributeSet
import android.view.ViewGroup
import androidx.coordinatorlayout.widget.CoordinatorLayout
@@ -10,6 +9,7 @@ import com.reddit.indicatorfastscroll.FastScrollItemIndicator
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME
+import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_SURNAME
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.activities.GroupContactsActivity
@@ -45,7 +45,6 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
var skipHashComparing = false
var forceListRedraw = false
- var wasLetterFastScrollerSetup = false
fun setupFragment(activity: SimpleActivity) {
config = activity.config
@@ -84,7 +83,6 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
this is GroupsFragment -> (fragment_list.adapter as GroupsAdapter).updateTextColor(color)
else -> (fragment_list.adapter as ContactsAdapter).apply {
updateTextColor(color)
- initDrawables()
}
}
}
@@ -112,8 +110,8 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
fun refreshContacts(contacts: ArrayList) {
if ((config.showTabs and CONTACTS_TAB_MASK == 0 && this is ContactsFragment && activity !is InsertOrEditContactActivity) ||
- (config.showTabs and FAVORITES_TAB_MASK == 0 && this is FavoritesFragment) ||
- (config.showTabs and GROUPS_TAB_MASK == 0 && this is GroupsFragment)) {
+ (config.showTabs and FAVORITES_TAB_MASK == 0 && this is FavoritesFragment) ||
+ (config.showTabs and GROUPS_TAB_MASK == 0 && this is GroupsFragment)) {
return
}
@@ -151,38 +149,10 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
setupContactsFavoritesAdapter(contacts)
contactsIgnoringSearch = (fragment_list?.adapter as? ContactsAdapter)?.contactItems ?: ArrayList()
- if (!wasLetterFastScrollerSetup) {
- wasLetterFastScrollerSetup = true
-
- val states = arrayOf(intArrayOf(android.R.attr.state_enabled),
- intArrayOf(-android.R.attr.state_enabled),
- intArrayOf(-android.R.attr.state_checked),
- intArrayOf(android.R.attr.state_pressed)
- )
-
- val textColor = config.textColor
- val colors = intArrayOf(textColor, textColor, textColor, textColor)
-
- val myList = ColorStateList(states, colors)
- letter_fastscroller.textColor = myList
-
- letter_fastscroller.setupWithRecyclerView(fragment_list, { position ->
- try {
- val name = contacts[position].getNameToDisplay()
- var character = if (name.isNotEmpty()) name.substring(0, 1) else ""
- if (!character.areLettersOnly()) {
- character = "#"
- }
-
- FastScrollItemIndicator.Text(character.toUpperCase(Locale.getDefault()))
- } catch (e: Exception) {
- FastScrollItemIndicator.Text("")
- }
- })
-
- letter_fastscroller_thumb.setupWithFastScroller(letter_fastscroller)
- letter_fastscroller_thumb.textColor = config.primaryColor.getContrastColor()
- }
+ letter_fastscroller.textColor = config.textColor.getColorStateList()
+ setupLetterFastscroller(contacts)
+ letter_fastscroller_thumb.setupWithFastScroller(letter_fastscroller)
+ letter_fastscroller_thumb.textColor = config.primaryColor.getContrastColor()
}
}
@@ -270,6 +240,31 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
}
+ private fun setupLetterFastscroller(contacts: ArrayList) {
+ letter_fastscroller.setupWithRecyclerView(fragment_list, { position ->
+ try {
+ val contact = contacts[position]
+ var name = when {
+ contact.isABusinessContact() -> contact.getFullCompany()
+ context.config.sorting and SORT_BY_SURNAME != 0 && contact.surname.isNotEmpty() -> contact.surname
+ context.config.sorting and SORT_BY_MIDDLE_NAME != 0 && contact.middleName.isNotEmpty() -> contact.middleName
+ context.config.sorting and SORT_BY_FIRST_NAME != 0 && contact.firstName.isNotEmpty() -> contact.firstName
+ context.config.startNameWithSurname -> contact.surname
+ else -> contact.firstName
+ }
+
+ if (name.isEmpty()) {
+ name = contact.getNameToDisplay()
+ }
+
+ val character = if (name.isNotEmpty()) name.substring(0, 1) else ""
+ FastScrollItemIndicator.Text(character.toUpperCase(Locale.getDefault()))
+ } catch (e: Exception) {
+ FastScrollItemIndicator.Text("")
+ }
+ })
+ }
+
fun fontSizeChanged() {
if (this is GroupsFragment) {
(fragment_list.adapter as? GroupsAdapter)?.apply {
@@ -321,6 +316,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
fragment_placeholder.beVisibleIf(filtered.isEmpty())
(adapter as? ContactsAdapter)?.updateItems(filtered, text.normalizeString())
+ setupLetterFastscroller(filtered)
} else if (adapter is GroupsAdapter) {
val filtered = groupsIgnoringSearch.filter {
it.title.contains(text, true)
@@ -343,6 +339,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
fun onSearchClosed() {
if (fragment_list.adapter is ContactsAdapter) {
(fragment_list.adapter as? ContactsAdapter)?.updateItems(contactsIgnoringSearch)
+ setupLetterFastscroller(contactsIgnoringSearch)
setupViewVisibility(contactsIgnoringSearch.isNotEmpty())
} else if (fragment_list.adapter is GroupsAdapter) {
(fragment_list.adapter as? GroupsAdapter)?.updateItems(groupsIgnoringSearch)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/CallManager.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/CallManager.kt
new file mode 100644
index 00000000..3be9e194
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/CallManager.kt
@@ -0,0 +1,64 @@
+package com.simplemobiletools.contacts.pro.helpers
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.net.Uri
+import android.telecom.Call
+import android.telecom.VideoProfile
+import com.simplemobiletools.commons.extensions.getNameFromPhoneNumber
+import com.simplemobiletools.commons.extensions.getPhotoUriFromPhoneNumber
+import com.simplemobiletools.contacts.pro.models.CallContact
+
+// inspired by https://github.com/Chooloo/call_manage
+@SuppressLint("NewApi")
+class CallManager {
+ companion object {
+ var call: Call? = null
+
+ fun accept() {
+ call?.answer(VideoProfile.STATE_AUDIO_ONLY)
+ }
+
+ fun reject() {
+ if (call != null) {
+ if (call!!.state == Call.STATE_RINGING) {
+ call!!.reject(false, null)
+ } else {
+ call!!.disconnect()
+ }
+ }
+ }
+
+ fun registerCallback(callback: Call.Callback) {
+ if (call != null) {
+ call!!.registerCallback(callback)
+ }
+ }
+
+ fun unregisterCallback(callback: Call.Callback) {
+ call?.unregisterCallback(callback)
+ }
+
+ fun getState() = if (call == null) {
+ Call.STATE_DISCONNECTED
+ } else {
+ call!!.state
+ }
+
+ fun getCallContact(context: Context): CallContact? {
+ val callContact = CallContact("", "")
+ if (call == null) {
+ return callContact
+ }
+
+ val uri = Uri.decode(call!!.details.handle.toString())
+ if (uri.startsWith("tel:")) {
+ val number = uri.substringAfter("tel:")
+ callContact.name = context.getNameFromPhoneNumber(number)
+ callContact.photoUri = context.getPhotoUriFromPhoneNumber(number)
+ }
+
+ return callContact
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt
index 7905d19b..76b047a7 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt
@@ -60,6 +60,14 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getBoolean(SHOW_DIALPAD_LETTERS, true)
set(showDialpadLetters) = prefs.edit().putBoolean(SHOW_DIALPAD_LETTERS, showDialpadLetters).apply()
+ var wasLocalAccountInitialized: Boolean
+ get() = prefs.getBoolean(WAS_LOCAL_ACCOUNT_INITIALIZED, false)
+ set(wasLocalAccountInitialized) = prefs.edit().putBoolean(WAS_LOCAL_ACCOUNT_INITIALIZED, wasLocalAccountInitialized).apply()
+
+ var lastExportPath: String
+ get() = prefs.getString(LAST_EXPORT_PATH, "")!!
+ set(lastExportPath) = prefs.edit().putString(LAST_EXPORT_PATH, lastExportPath).apply()
+
var speedDial: String
get() = prefs.getString(SPEED_DIAL, "")!!
set(speedDial) = prefs.edit().putString(SPEED_DIAL, speedDial).apply()
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt
index 6bbad364..5de1e921 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt
@@ -22,6 +22,8 @@ const val SHOW_CALL_CONFIRMATION = "show_call_confirmation"
const val SHOW_DIALPAD_BUTTON = "show_dialpad_button"
const val SHOW_DIALPAD_LETTERS = "show_dialpad_letters"
const val SPEED_DIAL = "speed_dial"
+const val LAST_EXPORT_PATH = "last_export_path"
+const val WAS_LOCAL_ACCOUNT_INITIALIZED = "was_local_account_initialized"
const val CONTACT_ID = "contact_id"
const val SMT_PRIVATE = "smt_private" // used at the contact source of local contacts hidden from other apps
@@ -31,7 +33,10 @@ const val IS_FROM_SIMPLE_CONTACTS = "is_from_simple_contacts"
const val ADD_NEW_CONTACT_NUMBER = "add_new_contact_number"
const val FIRST_CONTACT_ID = 1000000
const val FIRST_GROUP_ID = 10000L
-const val REQUEST_CODE_SET_DEFAULT_DIALER = 1
+
+private const val PATH = "com.simplemobiletools.contacts.action."
+const val ACCEPT_CALL = PATH + "accept_call"
+const val DECLINE_CALL = PATH + "decline_call"
// extras used at third party intents
const val KEY_PHONE = "phone"
@@ -114,7 +119,7 @@ const val TELEGRAM_PACKAGE = "org.telegram.messenger"
const val SIGNAL_PACKAGE = "org.thoughtcrime.securesms"
const val WHATSAPP_PACKAGE = "com.whatsapp"
-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())
fun getProperText(text: String, shouldNormalize: Boolean) = if (shouldNormalize) text.normalizeString() else text
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/ContactsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/ContactsHelper.kt
index 0eea63ef..0643b4a8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/ContactsHelper.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/ContactsHelper.kt
@@ -3,15 +3,12 @@ package com.simplemobiletools.contacts.pro.helpers
import android.accounts.Account
import android.accounts.AccountManager
import android.content.*
-import android.database.Cursor
import android.graphics.Bitmap
import android.net.Uri
import android.os.Handler
import android.os.Looper
-import android.provider.ContactsContract
-import android.provider.ContactsContract.CommonDataKinds
-import android.provider.ContactsContract.CommonDataKinds.Nickname
-import android.provider.ContactsContract.CommonDataKinds.Note
+import android.provider.ContactsContract.*
+import android.provider.ContactsContract.CommonDataKinds.*
import android.provider.MediaStore
import android.text.TextUtils
import android.util.SparseArray
@@ -20,6 +17,9 @@ import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.extensions.*
import com.simplemobiletools.contacts.pro.models.*
+import com.simplemobiletools.contacts.pro.models.Email
+import com.simplemobiletools.contacts.pro.models.Event
+import com.simplemobiletools.contacts.pro.models.Organization
import com.simplemobiletools.contacts.pro.overloads.times
import java.util.*
import kotlin.collections.ArrayList
@@ -99,7 +99,7 @@ class ContactsHelper(val context: Context) {
private fun getContentResolverAccounts(): HashSet {
val sources = HashSet()
- arrayOf(ContactsContract.Groups.CONTENT_URI, ContactsContract.Settings.CONTENT_URI, ContactsContract.RawContacts.CONTENT_URI).forEach {
+ arrayOf(Groups.CONTENT_URI, Settings.CONTENT_URI, RawContacts.CONTENT_URI).forEach {
fillSourcesFromUri(it, sources)
}
@@ -108,29 +108,20 @@ class ContactsHelper(val context: Context) {
private fun fillSourcesFromUri(uri: Uri, sources: HashSet) {
val projection = arrayOf(
- ContactsContract.RawContacts.ACCOUNT_NAME,
- ContactsContract.RawContacts.ACCOUNT_TYPE
+ RawContacts.ACCOUNT_NAME,
+ RawContacts.ACCOUNT_TYPE
)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, null, null, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val name = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
- val type = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE) ?: ""
- var publicName = name
- if (type == TELEGRAM_PACKAGE) {
- publicName += " (${context.getString(R.string.telegram)})"
- }
-
- val source = ContactSource(name, type, publicName)
- sources.add(source)
- } while (cursor.moveToNext())
+ context.queryCursor(uri, projection) { cursor ->
+ val name = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: ""
+ val type = cursor.getStringValue(RawContacts.ACCOUNT_TYPE) ?: ""
+ var publicName = name
+ if (type == TELEGRAM_PACKAGE) {
+ publicName += " (${context.getString(R.string.telegram)})"
}
- } catch (e: Exception) {
- } finally {
- cursor?.close()
+
+ val source = ContactSource(name, type, publicName)
+ sources.add(source)
}
}
@@ -140,64 +131,54 @@ class ContactsHelper(val context: Context) {
}
val ignoredSources = ignoredContactSources ?: context.config.ignoredContactSources
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = getContactProjection()
- val selection = "${ContactsContract.Data.MIMETYPE} = ? OR ${ContactsContract.Data.MIMETYPE} = ?"
- val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
+ val selection = "${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?"
+ val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
val sortOrder = getSortString()
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
- if (cursor?.moveToFirst() == true) {
- do {
- val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
- val accountType = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE) ?: ""
- if (ignoredSources.contains("$accountName:$accountType")) {
- continue
- }
-
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- var prefix = ""
- var firstName = ""
- var middleName = ""
- var surname = ""
- var suffix = ""
-
- // ignore names at Organization type contacts
- if (cursor.getStringValue(ContactsContract.Data.MIMETYPE) == CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) {
- prefix = cursor.getStringValue(CommonDataKinds.StructuredName.PREFIX) ?: ""
- firstName = cursor.getStringValue(CommonDataKinds.StructuredName.GIVEN_NAME) ?: ""
- middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
- surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
- suffix = cursor.getStringValue(CommonDataKinds.StructuredName.SUFFIX) ?: ""
- }
-
- val nickname = ""
- val photoUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_URI) ?: ""
- val numbers = ArrayList() // proper value is obtained below
- val emails = ArrayList()
- val addresses = ArrayList()
- val events = ArrayList()
- val starred = cursor.getIntValue(CommonDataKinds.StructuredName.STARRED)
- val contactId = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
- val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
- val notes = ""
- val groups = ArrayList()
- val organization = Organization("", "")
- val websites = ArrayList()
- val ims = ArrayList()
- val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, numbers, emails, addresses,
- events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims)
-
- contacts.put(id, contact)
- } while (cursor.moveToNext())
+ 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
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ 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 (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 nickname = ""
+ val photoUri = cursor.getStringValue(StructuredName.PHOTO_URI) ?: ""
+ val numbers = ArrayList() // proper value is obtained below
+ val emails = ArrayList()
+ val addresses = ArrayList()
+ val events = ArrayList()
+ 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()
+ val organization = Organization("", "")
+ val websites = ArrayList()
+ val ims = ArrayList()
+ val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, numbers, emails, addresses,
+ events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims)
+
+ contacts.put(id, contact)
}
val phoneNumbers = getPhoneNumbers(null)
@@ -269,41 +250,31 @@ class ContactsHelper(val context: Context) {
private fun getPhoneNumbers(contactId: Int? = null): SparseArray> {
val phoneNumbers = SparseArray>()
- val uri = CommonDataKinds.Phone.CONTENT_URI
+ val uri = Phone.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.Phone.NUMBER,
- CommonDataKinds.Phone.NORMALIZED_NUMBER,
- CommonDataKinds.Phone.TYPE,
- CommonDataKinds.Phone.LABEL
+ Data.RAW_CONTACT_ID,
+ Phone.NUMBER,
+ Phone.NORMALIZED_NUMBER,
+ Phone.TYPE,
+ Phone.LABEL
)
- val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
+ val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val number = cursor.getStringValue(CommonDataKinds.Phone.NUMBER) ?: continue
- val normalizedNumber = cursor.getStringValue(CommonDataKinds.Phone.NORMALIZED_NUMBER) ?: number.normalizeNumber()
- val type = cursor.getIntValue(CommonDataKinds.Phone.TYPE)
- val label = cursor.getStringValue(CommonDataKinds.Phone.LABEL) ?: ""
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val number = cursor.getStringValue(Phone.NUMBER) ?: return@queryCursor
+ val normalizedNumber = cursor.getStringValue(Phone.NORMALIZED_NUMBER) ?: number.normalizeNumber()
+ val type = cursor.getIntValue(Phone.TYPE)
+ val label = cursor.getStringValue(Phone.LABEL) ?: ""
- if (phoneNumbers[id] == null) {
- phoneNumbers.put(id, ArrayList())
- }
-
- val phoneNumber = PhoneNumber(number, type, label, normalizedNumber)
- phoneNumbers[id].add(phoneNumber)
- } while (cursor.moveToNext())
+ if (phoneNumbers[id] == null) {
+ phoneNumbers.put(id, ArrayList())
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ val phoneNumber = PhoneNumber(number, type, label, normalizedNumber)
+ phoneNumbers[id].add(phoneNumber)
}
return phoneNumbers
@@ -311,29 +282,19 @@ class ContactsHelper(val context: Context) {
private fun getNicknames(contactId: Int? = null): SparseArray {
val nicknames = SparseArray()
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- Nickname.NAME
+ Data.RAW_CONTACT_ID,
+ Nickname.NAME
)
val selection = getSourcesSelection(true, contactId != null)
val selectionArgs = getSourcesSelectionArgs(Nickname.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val nickname = cursor.getStringValue(Nickname.NAME) ?: continue
- nicknames.put(id, nickname)
- } while (cursor.moveToNext())
- }
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val nickname = cursor.getStringValue(Nickname.NAME) ?: return@queryCursor
+ nicknames.put(id, nickname)
}
return nicknames
@@ -343,36 +304,26 @@ class ContactsHelper(val context: Context) {
val emails = SparseArray>()
val uri = CommonDataKinds.Email.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.Email.DATA,
- CommonDataKinds.Email.TYPE,
- CommonDataKinds.Email.LABEL
+ Data.RAW_CONTACT_ID,
+ CommonDataKinds.Email.DATA,
+ CommonDataKinds.Email.TYPE,
+ CommonDataKinds.Email.LABEL
)
- val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
+ val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val email = cursor.getStringValue(CommonDataKinds.Email.DATA) ?: continue
- val type = cursor.getIntValue(CommonDataKinds.Email.TYPE)
- val label = cursor.getStringValue(CommonDataKinds.Email.LABEL) ?: ""
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val email = cursor.getStringValue(CommonDataKinds.Email.DATA) ?: return@queryCursor
+ val type = cursor.getIntValue(CommonDataKinds.Email.TYPE)
+ val label = cursor.getStringValue(CommonDataKinds.Email.LABEL) ?: ""
- if (emails[id] == null) {
- emails.put(id, ArrayList())
- }
-
- emails[id]!!.add(Email(email, type, label))
- } while (cursor.moveToNext())
+ if (emails[id] == null) {
+ emails.put(id, ArrayList())
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ emails[id]!!.add(Email(email, type, label))
}
return emails
@@ -380,38 +331,28 @@ class ContactsHelper(val context: Context) {
private fun getAddresses(contactId: Int? = null): SparseArray> {
val addresses = SparseArray>()
- val uri = CommonDataKinds.StructuredPostal.CONTENT_URI
+ val uri = StructuredPostal.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS,
- CommonDataKinds.StructuredPostal.TYPE,
- CommonDataKinds.StructuredPostal.LABEL
+ Data.RAW_CONTACT_ID,
+ StructuredPostal.FORMATTED_ADDRESS,
+ StructuredPostal.TYPE,
+ StructuredPostal.LABEL
)
- val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
+ val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val address = cursor.getStringValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) ?: continue
- val type = cursor.getIntValue(CommonDataKinds.StructuredPostal.TYPE)
- val label = cursor.getStringValue(CommonDataKinds.StructuredPostal.LABEL) ?: ""
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val address = cursor.getStringValue(StructuredPostal.FORMATTED_ADDRESS) ?: return@queryCursor
+ val type = cursor.getIntValue(StructuredPostal.TYPE)
+ val label = cursor.getStringValue(StructuredPostal.LABEL) ?: ""
- if (addresses[id] == null) {
- addresses.put(id, ArrayList())
- }
-
- addresses[id]!!.add(Address(address, type, label))
- } while (cursor.moveToNext())
+ if (addresses[id] == null) {
+ addresses.put(id, ArrayList())
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ addresses[id]!!.add(Address(address, type, label))
}
return addresses
@@ -419,38 +360,28 @@ class ContactsHelper(val context: Context) {
private fun getIMs(contactId: Int? = null): SparseArray> {
val IMs = SparseArray>()
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.Im.DATA,
- CommonDataKinds.Im.PROTOCOL,
- CommonDataKinds.Im.CUSTOM_PROTOCOL
+ Data.RAW_CONTACT_ID,
+ Im.DATA,
+ Im.PROTOCOL,
+ Im.CUSTOM_PROTOCOL
)
val selection = getSourcesSelection(true, contactId != null)
- val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Im.CONTENT_ITEM_TYPE, contactId)
+ val selectionArgs = getSourcesSelectionArgs(Im.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val IM = cursor.getStringValue(CommonDataKinds.Im.DATA) ?: continue
- val type = cursor.getIntValue(CommonDataKinds.Im.PROTOCOL)
- val label = cursor.getStringValue(CommonDataKinds.Im.CUSTOM_PROTOCOL) ?: ""
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val IM = cursor.getStringValue(Im.DATA) ?: return@queryCursor
+ val type = cursor.getIntValue(Im.PROTOCOL)
+ val label = cursor.getStringValue(Im.CUSTOM_PROTOCOL) ?: ""
- if (IMs[id] == null) {
- IMs.put(id, ArrayList())
- }
-
- IMs[id]!!.add(IM(IM, type, label))
- } while (cursor.moveToNext())
+ if (IMs[id] == null) {
+ IMs.put(id, ArrayList())
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ IMs[id]!!.add(IM(IM, type, label))
}
return IMs
@@ -458,36 +389,26 @@ class ContactsHelper(val context: Context) {
private fun getEvents(contactId: Int? = null): SparseArray> {
val events = SparseArray>()
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.Event.START_DATE,
- CommonDataKinds.Event.TYPE
+ Data.RAW_CONTACT_ID,
+ CommonDataKinds.Event.START_DATE,
+ CommonDataKinds.Event.TYPE
)
val selection = getSourcesSelection(true, contactId != null)
val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Event.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val startDate = cursor.getStringValue(CommonDataKinds.Event.START_DATE) ?: continue
- val type = cursor.getIntValue(CommonDataKinds.Event.TYPE)
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val startDate = cursor.getStringValue(CommonDataKinds.Event.START_DATE) ?: return@queryCursor
+ val type = cursor.getIntValue(CommonDataKinds.Event.TYPE)
- if (events[id] == null) {
- events.put(id, ArrayList())
- }
-
- events[id]!!.add(Event(startDate, type))
- } while (cursor.moveToNext())
+ if (events[id] == null) {
+ events.put(id, ArrayList())
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ events[id]!!.add(Event(startDate, type))
}
return events
@@ -495,29 +416,19 @@ class ContactsHelper(val context: Context) {
private fun getNotes(contactId: Int? = null): SparseArray {
val notes = SparseArray()
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- Note.NOTE
+ Data.RAW_CONTACT_ID,
+ Note.NOTE
)
val selection = getSourcesSelection(true, contactId != null)
val selectionArgs = getSourcesSelectionArgs(Note.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val note = cursor.getStringValue(Note.NOTE) ?: continue
- notes.put(id, note)
- } while (cursor.moveToNext())
- }
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val note = cursor.getStringValue(Note.NOTE) ?: return@queryCursor
+ notes.put(id, note)
}
return notes
@@ -525,36 +436,26 @@ class ContactsHelper(val context: Context) {
private fun getOrganizations(contactId: Int? = null): SparseArray {
val organizations = SparseArray()
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.Organization.COMPANY,
- CommonDataKinds.Organization.TITLE
+ Data.RAW_CONTACT_ID,
+ CommonDataKinds.Organization.COMPANY,
+ CommonDataKinds.Organization.TITLE
)
val selection = getSourcesSelection(true, contactId != null)
val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Organization.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val company = cursor.getStringValue(CommonDataKinds.Organization.COMPANY) ?: ""
- val title = cursor.getStringValue(CommonDataKinds.Organization.TITLE) ?: ""
- if (company.isEmpty() && title.isEmpty()) {
- continue
- }
-
- val organization = Organization(company, title)
- organizations.put(id, organization)
- } while (cursor.moveToNext())
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val company = cursor.getStringValue(CommonDataKinds.Organization.COMPANY) ?: ""
+ val title = cursor.getStringValue(CommonDataKinds.Organization.TITLE) ?: ""
+ if (company.isEmpty() && title.isEmpty()) {
+ return@queryCursor
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ val organization = Organization(company, title)
+ organizations.put(id, organization)
}
return organizations
@@ -562,34 +463,24 @@ class ContactsHelper(val context: Context) {
private fun getWebsites(contactId: Int? = null): SparseArray> {
val websites = SparseArray>()
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.Website.URL
+ Data.RAW_CONTACT_ID,
+ Website.URL
)
val selection = getSourcesSelection(true, contactId != null)
- val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Website.CONTENT_ITEM_TYPE, contactId)
+ val selectionArgs = getSourcesSelectionArgs(Website.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
- val url = cursor.getStringValue(CommonDataKinds.Website.URL) ?: continue
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
+ val url = cursor.getStringValue(Website.URL) ?: return@queryCursor
- if (websites[id] == null) {
- websites.put(id, ArrayList())
- }
-
- websites[id]!!.add(url)
- } while (cursor.moveToNext())
+ if (websites[id] == null) {
+ websites.put(id, ArrayList())
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ websites[id]!!.add(url)
}
return websites
@@ -601,35 +492,25 @@ class ContactsHelper(val context: Context) {
return groups
}
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Data.CONTACT_ID,
- ContactsContract.Data.DATA1
+ Data.CONTACT_ID,
+ Data.DATA1
)
val selection = getSourcesSelection(true, contactId != null, false)
- val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE, contactId)
+ val selectionArgs = getSourcesSelectionArgs(GroupMembership.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
- val newRowId = cursor.getLongValue(ContactsContract.Data.DATA1)
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getIntValue(Data.CONTACT_ID)
+ val newRowId = cursor.getLongValue(Data.DATA1)
- val groupTitle = storedGroups.firstOrNull { it.id == newRowId }?.title ?: continue
- val group = Group(newRowId, groupTitle)
- if (groups[id] == null) {
- groups.put(id, ArrayList())
- }
- groups[id]!!.add(group)
- } while (cursor.moveToNext())
+ val groupTitle = storedGroups.firstOrNull { it.id == newRowId }?.title ?: return@queryCursor
+ val group = Group(newRowId, groupTitle)
+ if (groups[id] == null) {
+ groups.put(id, ArrayList())
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+ groups[id]!!.add(group)
}
return groups
@@ -640,20 +521,20 @@ class ContactsHelper(val context: Context) {
private fun getSourcesSelection(addMimeType: Boolean = false, addContactId: Boolean = false, useRawContactId: Boolean = true): String {
val strings = ArrayList()
if (addMimeType) {
- strings.add("${ContactsContract.Data.MIMETYPE} = ?")
+ strings.add("${Data.MIMETYPE} = ?")
}
if (addContactId) {
- strings.add("${if (useRawContactId) ContactsContract.Data.RAW_CONTACT_ID else ContactsContract.Data.CONTACT_ID} = ?")
+ strings.add("${if (useRawContactId) Data.RAW_CONTACT_ID else Data.CONTACT_ID} = ?")
} else {
// sometimes local device storage has null account_name, handle it properly
val accountnameString = StringBuilder()
if (displayContactSources.contains("")) {
accountnameString.append("(")
}
- accountnameString.append("${ContactsContract.RawContacts.ACCOUNT_NAME} IN (${getQuestionMarks()})")
+ accountnameString.append("${RawContacts.ACCOUNT_NAME} IN (${getQuestionMarks()})")
if (displayContactSources.contains("")) {
- accountnameString.append(" OR ${ContactsContract.RawContacts.ACCOUNT_NAME} IS NULL)")
+ accountnameString.append(" OR ${RawContacts.ACCOUNT_NAME} IS NULL)")
}
strings.add(accountnameString.toString())
}
@@ -698,36 +579,26 @@ class ContactsHelper(val context: Context) {
return groups
}
- val uri = ContactsContract.Groups.CONTENT_URI
+ val uri = Groups.CONTENT_URI
val projection = arrayOf(
- ContactsContract.Groups._ID,
- ContactsContract.Groups.TITLE,
- ContactsContract.Groups.SYSTEM_ID
+ Groups._ID,
+ Groups.TITLE,
+ Groups.SYSTEM_ID
)
- val selection = "${ContactsContract.Groups.AUTO_ADD} = ? AND ${ContactsContract.Groups.FAVORITES} = ?"
+ val selection = "${Groups.AUTO_ADD} = ? AND ${Groups.FAVORITES} = ?"
val selectionArgs = arrayOf("0", "0")
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- do {
- val id = cursor.getLongValue(ContactsContract.Groups._ID)
- val title = cursor.getStringValue(ContactsContract.Groups.TITLE) ?: continue
+ context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val id = cursor.getLongValue(Groups._ID)
+ val title = cursor.getStringValue(Groups.TITLE) ?: return@queryCursor
- val systemId = cursor.getStringValue(ContactsContract.Groups.SYSTEM_ID)
- if (groups.map { it.title }.contains(title) && systemId != null) {
- continue
- }
-
- groups.add(Group(id, title))
- } while (cursor.moveToNext())
+ val systemId = cursor.getStringValue(Groups.SYSTEM_ID)
+ if (groups.map { it.title }.contains(title) && systemId != null) {
+ return@queryCursor
}
- } catch (e: Exception) {
- context.showErrorToast(e)
- } finally {
- cursor?.close()
+
+ groups.add(Group(id, title))
}
return groups
}
@@ -741,16 +612,16 @@ class ContactsHelper(val context: Context) {
}
val operations = ArrayList()
- ContentProviderOperation.newInsert(ContactsContract.Groups.CONTENT_URI).apply {
- withValue(ContactsContract.Groups.TITLE, title)
- withValue(ContactsContract.Groups.GROUP_VISIBLE, 1)
- withValue(ContactsContract.Groups.ACCOUNT_NAME, accountName)
- withValue(ContactsContract.Groups.ACCOUNT_TYPE, accountType)
+ ContentProviderOperation.newInsert(Groups.CONTENT_URI).apply {
+ withValue(Groups.TITLE, title)
+ withValue(Groups.GROUP_VISIBLE, 1)
+ withValue(Groups.ACCOUNT_NAME, accountName)
+ withValue(Groups.ACCOUNT_TYPE, accountType)
operations.add(build())
}
try {
- val results = context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ val results = context.contentResolver.applyBatch(AUTHORITY, operations)
val rawId = ContentUris.parseId(results[0].uri)
return Group(rawId, title)
} catch (e: Exception) {
@@ -761,16 +632,16 @@ class ContactsHelper(val context: Context) {
fun renameGroup(group: Group) {
val operations = ArrayList()
- ContentProviderOperation.newUpdate(ContactsContract.Groups.CONTENT_URI).apply {
- val selection = "${ContactsContract.Groups._ID} = ?"
+ ContentProviderOperation.newUpdate(Groups.CONTENT_URI).apply {
+ val selection = "${Groups._ID} = ?"
val selectionArgs = arrayOf(group.id.toString())
withSelection(selection, selectionArgs)
- withValue(ContactsContract.Groups.TITLE, group.title)
+ withValue(Groups.TITLE, group.title)
operations.add(build())
}
try {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ context.contentResolver.applyBatch(AUTHORITY, operations)
} catch (e: Exception) {
context.showErrorToast(e)
}
@@ -778,14 +649,14 @@ class ContactsHelper(val context: Context) {
fun deleteGroup(id: Long) {
val operations = ArrayList()
- val uri = ContentUris.withAppendedId(ContactsContract.Groups.CONTENT_URI, id).buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .build()
+ val uri = ContentUris.withAppendedId(Groups.CONTENT_URI, id).buildUpon()
+ .appendQueryParameter(CALLER_IS_SYNCADAPTER, "true")
+ .build()
operations.add(ContentProviderOperation.newDelete(uri).build())
try {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ context.contentResolver.applyBatch(AUTHORITY, operations)
} catch (e: Exception) {
context.showErrorToast(e)
}
@@ -798,26 +669,26 @@ class ContactsHelper(val context: Context) {
return LocalContactsHelper(context).getContactWithId(id)
}
- val selection = "(${ContactsContract.Data.MIMETYPE} = ? OR ${ContactsContract.Data.MIMETYPE} = ?) AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
- val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString())
+ val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.RAW_CONTACT_ID} = ?"
+ val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString())
return parseContactCursor(selection, selectionArgs)
}
fun getContactWithLookupKey(key: String): Contact? {
- val selection = "(${ContactsContract.Data.MIMETYPE} = ? OR ${ContactsContract.Data.MIMETYPE} = ?) AND ${ContactsContract.Data.LOOKUP_KEY} = ?"
- val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, key)
+ val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.LOOKUP_KEY} = ?"
+ val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, key)
return parseContactCursor(selection, selectionArgs)
}
private fun parseContactCursor(selection: String, selectionArgs: Array): Contact? {
val storedGroups = getStoredGroupsSync()
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = getContactProjection()
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
+
+ val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
+ cursor?.use {
+ if (cursor.moveToFirst()) {
+ val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
var prefix = ""
var firstName = ""
@@ -826,34 +697,32 @@ class ContactsHelper(val context: Context) {
var suffix = ""
// ignore names at Organization type contacts
- if (cursor.getStringValue(ContactsContract.Data.MIMETYPE) == CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) {
- prefix = cursor.getStringValue(CommonDataKinds.StructuredName.PREFIX) ?: ""
- firstName = cursor.getStringValue(CommonDataKinds.StructuredName.GIVEN_NAME) ?: ""
- middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
- surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
- suffix = cursor.getStringValue(CommonDataKinds.StructuredName.SUFFIX) ?: ""
+ 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 nickname = getNicknames(id)[id] ?: ""
- val photoUri = cursor.getStringValue(CommonDataKinds.Phone.PHOTO_URI) ?: ""
+ val photoUri = cursor.getStringValue(Phone.PHOTO_URI) ?: ""
val number = getPhoneNumbers(id)[id] ?: ArrayList()
val emails = getEmails(id)[id] ?: ArrayList()
val addresses = getAddresses(id)[id] ?: ArrayList()
val events = getEvents(id)[id] ?: ArrayList()
val notes = getNotes(id)[id] ?: ""
- val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
- val starred = cursor.getIntValue(CommonDataKinds.StructuredName.STARRED)
- val contactId = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
+ val accountName = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: ""
+ val starred = cursor.getIntValue(StructuredName.STARRED)
+ val contactId = cursor.getIntValue(Data.CONTACT_ID)
val groups = getContactGroups(storedGroups, contactId)[contactId] ?: ArrayList()
- val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
+ val thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
val organization = getOrganizations(id)[id] ?: Organization("", "")
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)
}
- } finally {
- cursor?.close()
}
return null
@@ -865,21 +734,40 @@ class ContactsHelper(val context: Context) {
}
}
- fun getContactSourcesSync(): ArrayList {
+ private fun getContactSourcesSync(): ArrayList {
val sources = getDeviceContactSources()
sources.add(context.getPrivateContactSource())
return ArrayList(sources)
}
+ fun getSaveableContactSources(callback: (ArrayList) -> Unit) {
+ ensureBackgroundThread {
+ val ignoredTypes = arrayListOf(
+ SIGNAL_PACKAGE,
+ TELEGRAM_PACKAGE,
+ WHATSAPP_PACKAGE
+ )
+
+ val contactSources = getContactSourcesSync()
+ val filteredSources = contactSources.filter { !ignoredTypes.contains(it.type) }.toMutableList() as ArrayList
+ callback(filteredSources)
+ }
+ }
+
fun getDeviceContactSources(): LinkedHashSet {
val sources = LinkedHashSet()
if (!context.hasContactPermissions()) {
return sources
}
+ if (!context.config.wasLocalAccountInitialized) {
+ initializeLocalPhoneAccount()
+ context.config.wasLocalAccountInitialized = true
+ }
+
val accounts = AccountManager.get(context).accounts
accounts.forEach {
- if (ContentResolver.getIsSyncable(it, ContactsContract.AUTHORITY) == 1) {
+ if (ContentResolver.getIsSyncable(it, AUTHORITY) == 1) {
var publicName = it.name
if (it.type == TELEGRAM_PACKAGE) {
publicName += " (${context.getString(R.string.telegram)})"
@@ -897,47 +785,63 @@ class ContactsHelper(val context: Context) {
return sources
}
+ // make sure the local Phone contact source is initialized and available
+ // https://stackoverflow.com/a/6096508/1967672
+ private fun initializeLocalPhoneAccount() {
+ try {
+ val operations = ArrayList()
+ ContentProviderOperation.newInsert(RawContacts.CONTENT_URI).apply {
+ withValue(RawContacts.ACCOUNT_NAME, null)
+ withValue(RawContacts.ACCOUNT_TYPE, null)
+ operations.add(build())
+ }
+
+ val results = context.contentResolver.applyBatch(AUTHORITY, operations)
+ val rawContactUri = results.firstOrNull()?.uri ?: return
+ context.contentResolver.delete(rawContactUri, null, null)
+ } catch (ignored: Exception) {
+ }
+ }
+
private fun getContactSourceType(accountName: String) = getDeviceContactSources().firstOrNull { it.name == accountName }?.type ?: ""
private fun getContactProjection() = arrayOf(
- ContactsContract.Data.MIMETYPE,
- ContactsContract.Data.CONTACT_ID,
- ContactsContract.Data.RAW_CONTACT_ID,
- CommonDataKinds.StructuredName.PREFIX,
- CommonDataKinds.StructuredName.GIVEN_NAME,
- CommonDataKinds.StructuredName.MIDDLE_NAME,
- CommonDataKinds.StructuredName.FAMILY_NAME,
- CommonDataKinds.StructuredName.SUFFIX,
- CommonDataKinds.StructuredName.PHOTO_URI,
- CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI,
- CommonDataKinds.StructuredName.STARRED,
- ContactsContract.RawContacts.ACCOUNT_NAME,
- ContactsContract.RawContacts.ACCOUNT_TYPE
+ Data.MIMETYPE,
+ Data.CONTACT_ID,
+ Data.RAW_CONTACT_ID,
+ StructuredName.PREFIX,
+ StructuredName.GIVEN_NAME,
+ StructuredName.MIDDLE_NAME,
+ StructuredName.FAMILY_NAME,
+ StructuredName.SUFFIX,
+ StructuredName.PHOTO_URI,
+ StructuredName.PHOTO_THUMBNAIL_URI,
+ StructuredName.STARRED,
+ RawContacts.ACCOUNT_NAME,
+ RawContacts.ACCOUNT_TYPE
)
private fun getSortString(): String {
val sorting = context.config.sorting
return when {
- sorting and SORT_BY_FIRST_NAME != 0 -> "${CommonDataKinds.StructuredName.GIVEN_NAME} COLLATE NOCASE"
- sorting and SORT_BY_MIDDLE_NAME != 0 -> "${CommonDataKinds.StructuredName.MIDDLE_NAME} COLLATE NOCASE"
- sorting and SORT_BY_SURNAME != 0 -> "${CommonDataKinds.StructuredName.FAMILY_NAME} COLLATE NOCASE"
- else -> CommonDataKinds.Phone.NUMBER
+ sorting and SORT_BY_FIRST_NAME != 0 -> "${StructuredName.GIVEN_NAME} COLLATE NOCASE"
+ sorting and SORT_BY_MIDDLE_NAME != 0 -> "${StructuredName.MIDDLE_NAME} COLLATE NOCASE"
+ sorting and SORT_BY_SURNAME != 0 -> "${StructuredName.FAMILY_NAME} COLLATE NOCASE"
+ else -> Phone.NUMBER
}
}
private fun getRealContactId(id: Long): Int {
- val uri = ContactsContract.Data.CONTENT_URI
+ val uri = Data.CONTENT_URI
val projection = getContactProjection()
- val selection = "(${ContactsContract.Data.MIMETYPE} = ? OR ${ContactsContract.Data.MIMETYPE} = ?) AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
- val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString())
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- return cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
+ val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.RAW_CONTACT_ID} = ?"
+ val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString())
+
+ val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
+ cursor?.use {
+ if (cursor.moveToFirst()) {
+ return cursor.getIntValue(Data.CONTACT_ID)
}
- } finally {
- cursor?.close()
}
return 0
@@ -951,58 +855,58 @@ class ContactsHelper(val context: Context) {
try {
val operations = ArrayList()
- ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND (${ContactsContract.Data.MIMETYPE} = ? OR ${ContactsContract.Data.MIMETYPE} = ?)"
- val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
+ 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)
withSelection(selection, selectionArgs)
- withValue(CommonDataKinds.StructuredName.PREFIX, contact.prefix)
- withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
- withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
- withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
- withValue(CommonDataKinds.StructuredName.SUFFIX, contact.suffix)
+ withValue(StructuredName.PREFIX, contact.prefix)
+ withValue(StructuredName.GIVEN_NAME, contact.firstName)
+ withValue(StructuredName.MIDDLE_NAME, contact.middleName)
+ withValue(StructuredName.FAMILY_NAME, contact.surname)
+ withValue(StructuredName.SUFFIX, contact.suffix)
operations.add(build())
}
// delete nickname
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), Nickname.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add nickname
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE)
withValue(Nickname.NAME, contact.nickname)
operations.add(build())
}
// delete phone numbers
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
- val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
+ val selectionArgs = arrayOf(contact.id.toString(), Phone.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add phone numbers
contact.phoneNumbers.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Phone.NUMBER, it.value)
- withValue(CommonDataKinds.Phone.NORMALIZED_NUMBER, it.normalizedNumber)
- withValue(CommonDataKinds.Phone.TYPE, it.type)
- withValue(CommonDataKinds.Phone.LABEL, it.label)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
+ withValue(Phone.NUMBER, it.value)
+ withValue(Phone.NORMALIZED_NUMBER, it.normalizedNumber)
+ withValue(Phone.TYPE, it.type)
+ withValue(Phone.LABEL, it.label)
operations.add(build())
}
}
// delete emails
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Email.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
@@ -1010,9 +914,9 @@ class ContactsHelper(val context: Context) {
// add emails
contact.emails.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Email.DATA, it.value)
withValue(CommonDataKinds.Email.TYPE, it.type)
withValue(CommonDataKinds.Email.LABEL, it.label)
@@ -1021,48 +925,48 @@ class ContactsHelper(val context: Context) {
}
// delete addresses
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
- val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
+ val selectionArgs = arrayOf(contact.id.toString(), StructuredPostal.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add addresses
contact.addresses.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, it.value)
- withValue(CommonDataKinds.StructuredPostal.TYPE, it.type)
- withValue(CommonDataKinds.StructuredPostal.LABEL, it.label)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE)
+ withValue(StructuredPostal.FORMATTED_ADDRESS, it.value)
+ withValue(StructuredPostal.TYPE, it.type)
+ withValue(StructuredPostal.LABEL, it.label)
operations.add(build())
}
}
// delete IMs
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
- val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Im.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
+ val selectionArgs = arrayOf(contact.id.toString(), Im.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add IMs
contact.IMs.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Im.DATA, it.value)
- withValue(CommonDataKinds.Im.PROTOCOL, it.type)
- withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, it.label)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE)
+ withValue(Im.DATA, it.value)
+ withValue(Im.PROTOCOL, it.type)
+ withValue(Im.CUSTOM_PROTOCOL, it.label)
operations.add(build())
}
}
// delete events
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Event.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
@@ -1070,9 +974,9 @@ class ContactsHelper(val context: Context) {
// add events
contact.events.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Event.START_DATE, it.value)
withValue(CommonDataKinds.Event.TYPE, it.type)
operations.add(build())
@@ -1080,33 +984,33 @@ class ContactsHelper(val context: Context) {
}
// delete notes
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), Note.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add notes
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
withValue(Note.NOTE, contact.notes)
operations.add(build())
}
// delete organization
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add organization
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
+ 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)
@@ -1115,20 +1019,20 @@ class ContactsHelper(val context: Context) {
}
// delete websites
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
- val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Website.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
+ val selectionArgs = arrayOf(contact.id.toString(), Website.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add websites
contact.websites.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Website.URL, it)
- withValue(CommonDataKinds.Website.TYPE, DEFAULT_WEBSITE_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE)
+ withValue(Website.URL, it)
+ withValue(Website.TYPE, DEFAULT_WEBSITE_TYPE)
operations.add(build())
}
}
@@ -1137,9 +1041,9 @@ class ContactsHelper(val context: Context) {
val relevantGroupIDs = getStoredGroupsSync().map { it.id }
if (relevantGroupIDs.isNotEmpty()) {
val IDsString = TextUtils.join(",", relevantGroupIDs)
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.DATA1} IN ($IDsString)"
- val selectionArgs = arrayOf(contact.contactId.toString(), CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? AND ${Data.DATA1} IN ($IDsString)"
+ val selectionArgs = arrayOf(contact.contactId.toString(), GroupMembership.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
@@ -1147,19 +1051,19 @@ class ContactsHelper(val context: Context) {
// add groups
contact.groups.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.GroupMembership.GROUP_ROW_ID, it.id)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
+ withValue(GroupMembership.GROUP_ROW_ID, it.id)
operations.add(build())
}
}
// favorite
try {
- val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contact.contactId.toString())
+ val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, contact.contactId.toString())
val contentValues = ContentValues(1)
- contentValues.put(ContactsContract.Contacts.STARRED, contact.starred)
+ contentValues.put(Contacts.STARRED, contact.starred)
context.contentResolver.update(uri, contentValues, null, null)
} catch (e: Exception) {
context.showErrorToast(e)
@@ -1171,7 +1075,7 @@ class ContactsHelper(val context: Context) {
PHOTO_REMOVED -> removePhoto(contact, operations)
}
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ context.contentResolver.applyBatch(AUTHORITY, operations)
return true
} catch (e: Exception) {
context.showErrorToast(e)
@@ -1192,10 +1096,10 @@ class ContactsHelper(val context: Context) {
val fullSizePhotoData = bitmap.getByteArray()
bitmap.recycle()
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, contact.id)
+ withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE)
+ withValue(Photo.PHOTO, scaledSizePhotoData)
operations.add(build())
}
@@ -1205,9 +1109,9 @@ class ContactsHelper(val context: Context) {
}
private fun removePhoto(contact: Contact, operations: ArrayList): ArrayList {
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?"
- val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ?"
+ val selectionArgs = arrayOf(contact.id.toString(), Photo.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
@@ -1216,44 +1120,48 @@ class ContactsHelper(val context: Context) {
}
fun addContactsToGroup(contacts: ArrayList, groupId: Long) {
- val operations = ArrayList()
- contacts.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValue(ContactsContract.Data.RAW_CONTACT_ID, it.id)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.GroupMembership.GROUP_ROW_ID, groupId)
- operations.add(build())
- }
-
- if (operations.size % BATCH_SIZE == 0) {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
- operations.clear()
- }
- }
-
try {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ val operations = ArrayList()
+ contacts.forEach {
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValue(Data.RAW_CONTACT_ID, it.id)
+ withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
+ withValue(GroupMembership.GROUP_ROW_ID, groupId)
+ operations.add(build())
+ }
+
+ if (operations.size % BATCH_SIZE == 0) {
+ context.contentResolver.applyBatch(AUTHORITY, operations)
+ operations.clear()
+ }
+ }
+
+ context.contentResolver.applyBatch(AUTHORITY, operations)
} catch (e: Exception) {
context.showErrorToast(e)
}
}
fun removeContactsFromGroup(contacts: ArrayList, groupId: Long) {
- val operations = ArrayList()
- contacts.forEach {
- ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
- val selection = "${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.DATA1} = ?"
- val selectionArgs = arrayOf(it.contactId.toString(), CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE, groupId.toString())
- withSelection(selection, selectionArgs)
- operations.add(build())
- }
+ try {
+ val operations = ArrayList()
+ contacts.forEach {
+ ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
+ val selection = "${Data.CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? AND ${Data.DATA1} = ?"
+ val selectionArgs = arrayOf(it.contactId.toString(), GroupMembership.CONTENT_ITEM_TYPE, groupId.toString())
+ withSelection(selection, selectionArgs)
+ operations.add(build())
+ }
- if (operations.size % BATCH_SIZE == 0) {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
- operations.clear()
+ if (operations.size % BATCH_SIZE == 0) {
+ context.contentResolver.applyBatch(AUTHORITY, operations)
+ operations.clear()
+ }
}
+ context.contentResolver.applyBatch(AUTHORITY, operations)
+ } catch (e: Exception) {
+ context.showErrorToast(e)
}
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
}
fun insertContact(contact: Contact): Boolean {
@@ -1263,50 +1171,50 @@ class ContactsHelper(val context: Context) {
try {
val operations = ArrayList()
- ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).apply {
- withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.source)
- withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, getContactSourceType(contact.source))
+ ContentProviderOperation.newInsert(RawContacts.CONTENT_URI).apply {
+ withValue(RawContacts.ACCOUNT_NAME, contact.source)
+ withValue(RawContacts.ACCOUNT_TYPE, getContactSourceType(contact.source))
operations.add(build())
}
// names
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.StructuredName.PREFIX, contact.prefix)
- withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
- withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
- withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
- withValue(CommonDataKinds.StructuredName.SUFFIX, contact.suffix)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
+ withValue(StructuredName.PREFIX, contact.prefix)
+ withValue(StructuredName.GIVEN_NAME, contact.firstName)
+ withValue(StructuredName.MIDDLE_NAME, contact.middleName)
+ withValue(StructuredName.FAMILY_NAME, contact.surname)
+ withValue(StructuredName.SUFFIX, contact.suffix)
operations.add(build())
}
// nickname
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE)
withValue(Nickname.NAME, contact.nickname)
operations.add(build())
}
// phone numbers
contact.phoneNumbers.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Phone.NUMBER, it.value)
- withValue(CommonDataKinds.Phone.NORMALIZED_NUMBER, it.normalizedNumber)
- withValue(CommonDataKinds.Phone.TYPE, it.type)
- withValue(CommonDataKinds.Phone.LABEL, it.label)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
+ withValue(Phone.NUMBER, it.value)
+ withValue(Phone.NORMALIZED_NUMBER, it.normalizedNumber)
+ withValue(Phone.TYPE, it.type)
+ withValue(Phone.LABEL, it.label)
operations.add(build())
}
}
// emails
contact.emails.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Email.DATA, it.value)
withValue(CommonDataKinds.Email.TYPE, it.type)
withValue(CommonDataKinds.Email.LABEL, it.label)
@@ -1316,33 +1224,33 @@ class ContactsHelper(val context: Context) {
// addresses
contact.addresses.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, it.value)
- withValue(CommonDataKinds.StructuredPostal.TYPE, it.type)
- withValue(CommonDataKinds.StructuredPostal.LABEL, it.label)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE)
+ withValue(StructuredPostal.FORMATTED_ADDRESS, it.value)
+ withValue(StructuredPostal.TYPE, it.type)
+ withValue(StructuredPostal.LABEL, it.label)
operations.add(build())
}
}
// IMs
contact.IMs.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Im.DATA, it.value)
- withValue(CommonDataKinds.Im.PROTOCOL, it.type)
- withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, it.label)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE)
+ withValue(Im.DATA, it.value)
+ withValue(Im.PROTOCOL, it.type)
+ withValue(Im.CUSTOM_PROTOCOL, it.label)
operations.add(build())
}
}
// events
contact.events.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Event.START_DATE, it.value)
withValue(CommonDataKinds.Event.TYPE, it.type)
operations.add(build())
@@ -1350,18 +1258,18 @@ class ContactsHelper(val context: Context) {
}
// notes
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
withValue(Note.NOTE, contact.notes)
operations.add(build())
}
// organization
if (contact.organization.isNotEmpty()) {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ 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)
@@ -1372,53 +1280,43 @@ class ContactsHelper(val context: Context) {
// websites
contact.websites.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Website.URL, it)
- withValue(CommonDataKinds.Website.TYPE, DEFAULT_WEBSITE_TYPE)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE)
+ withValue(Website.URL, it)
+ withValue(Website.TYPE, DEFAULT_WEBSITE_TYPE)
operations.add(build())
}
}
// groups
contact.groups.forEach {
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.GroupMembership.GROUP_ROW_ID, it.id)
+ ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
+ withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
+ withValue(GroupMembership.GROUP_ROW_ID, it.id)
operations.add(build())
}
}
// photo (inspired by https://gist.github.com/slightfoot/5985900)
var fullSizePhotoData: ByteArray? = null
- var scaledSizePhotoData: ByteArray?
if (contact.photoUri.isNotEmpty()) {
val photoUri = Uri.parse(contact.photoUri)
- val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, photoUri)
-
- val thumbnailSize = context.getPhotoThumbnailSize()
- val scaledPhoto = Bitmap.createScaledBitmap(bitmap, thumbnailSize, thumbnailSize, false)
- scaledSizePhotoData = scaledPhoto.getByteArray()
-
- fullSizePhotoData = bitmap.getByteArray()
- scaledPhoto.recycle()
- bitmap.recycle()
-
- ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
- withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
- withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
- operations.add(build())
- }
+ fullSizePhotoData = context.contentResolver.openInputStream(photoUri)?.readBytes()
}
- val results: Array
- try {
- results = context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
- } finally {
- scaledSizePhotoData = null
+ val results = context.contentResolver.applyBatch(AUTHORITY, operations)
+
+ // storing contacts on some devices seems to be messed up and they move on Phone instead, or disappear completely
+ // try storing a lighter contact version with this oldschool version too just so it wont disappear, future edits work well
+ if (getContactSourceType(contact.source).contains(".sim")) {
+ val simUri = Uri.parse("content://icc/adn")
+ ContentValues().apply {
+ put("number", contact.phoneNumbers.firstOrNull()?.value ?: "")
+ put("tag", contact.getNameToDisplay())
+ context.contentResolver.insert(simUri, this)
+ }
}
// fullsize photo
@@ -1430,9 +1328,9 @@ class ContactsHelper(val context: Context) {
// favorite
val userId = getRealContactId(rawId)
if (userId != 0 && contact.starred == 1) {
- val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, userId.toString())
+ val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, userId.toString())
val contentValues = ContentValues(1)
- contentValues.put(ContactsContract.Contacts.STARRED, contact.starred)
+ contentValues.put(Contacts.STARRED, contact.starred)
context.contentResolver.update(uri, contentValues, null, null)
}
@@ -1444,8 +1342,8 @@ class ContactsHelper(val context: Context) {
}
private fun addFullSizePhoto(contactId: Long, fullSizePhotoData: ByteArray) {
- val baseUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, contactId)
- val displayPhotoUri = Uri.withAppendedPath(baseUri, ContactsContract.RawContacts.DisplayPhoto.CONTENT_DIRECTORY)
+ val baseUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, contactId)
+ val displayPhotoUri = Uri.withAppendedPath(baseUri, RawContacts.DisplayPhoto.CONTENT_DIRECTORY)
val fileDescriptor = context.contentResolver.openAssetFileDescriptor(displayPhotoUri, "rw")
val photoStream = fileDescriptor!!.createOutputStream()
photoStream.write(fullSizePhotoData)
@@ -1454,38 +1352,35 @@ class ContactsHelper(val context: Context) {
}
fun getContactLookupKey(contactId: String): String {
- val uri = ContactsContract.Data.CONTENT_URI
- val projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.LOOKUP_KEY)
- val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
- val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
- val lookupKey = cursor.getStringValue(ContactsContract.Data.LOOKUP_KEY)
+ val uri = Data.CONTENT_URI
+ val projection = arrayOf(Data.CONTACT_ID, Data.LOOKUP_KEY)
+ val selection = "${Data.MIMETYPE} = ? AND ${Data.RAW_CONTACT_ID} = ?"
+ val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, contactId)
+
+ val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
+ cursor?.use {
+ if (cursor.moveToFirst()) {
+ val id = cursor.getIntValue(Data.CONTACT_ID)
+ val lookupKey = cursor.getStringValue(Data.LOOKUP_KEY)
return "$lookupKey/$id"
}
- } finally {
- cursor?.close()
}
+
return ""
}
fun getContactMimeTypeId(contactId: String, mimeType: String): String {
- val uri = ContactsContract.Data.CONTENT_URI
- val projection = arrayOf(ContactsContract.Data._ID, ContactsContract.Data.RAW_CONTACT_ID, ContactsContract.Data.MIMETYPE)
- val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
+ val uri = Data.CONTENT_URI
+ val projection = arrayOf(Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE)
+ val selection = "${Data.MIMETYPE} = ? AND ${Data.RAW_CONTACT_ID} = ?"
val selectionArgs = arrayOf(mimeType, contactId)
- var cursor: Cursor? = null
- try {
- cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor?.moveToFirst() == true) {
- return cursor.getStringValue(ContactsContract.Data._ID)
+
+ val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
+ cursor?.use {
+ if (cursor.moveToFirst()) {
+ return cursor.getStringValue(Data._ID)
}
- } finally {
- cursor?.close()
}
return ""
}
@@ -1512,18 +1407,18 @@ class ContactsHelper(val context: Context) {
try {
val operations = ArrayList()
contacts.filter { !it.isPrivate() }.map { it.contactId.toString() }.forEach {
- val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, it)
+ val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, it)
ContentProviderOperation.newUpdate(uri).apply {
- withValue(ContactsContract.Contacts.STARRED, if (addToFavorites) 1 else 0)
+ withValue(Contacts.STARRED, if (addToFavorites) 1 else 0)
operations.add(build())
}
if (operations.size % BATCH_SIZE == 0) {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ context.contentResolver.applyBatch(AUTHORITY, operations)
operations.clear()
}
}
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ context.contentResolver.applyBatch(AUTHORITY, operations)
} catch (e: Exception) {
context.showErrorToast(e)
}
@@ -1558,22 +1453,22 @@ class ContactsHelper(val context: Context) {
return try {
val operations = ArrayList()
- val selection = "${ContactsContract.RawContacts._ID} = ?"
+ val selection = "${RawContacts._ID} = ?"
contacts.filter { !it.isPrivate() }.forEach {
- ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI).apply {
+ ContentProviderOperation.newDelete(RawContacts.CONTENT_URI).apply {
val selectionArgs = arrayOf(it.id.toString())
withSelection(selection, selectionArgs)
operations.add(build())
}
if (operations.size % BATCH_SIZE == 0) {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ context.contentResolver.applyBatch(AUTHORITY, operations)
operations.clear()
}
}
if (context.hasPermission(PERMISSION_WRITE_CONTACTS)) {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
+ context.contentResolver.applyBatch(AUTHORITY, operations)
}
true
} catch (e: Exception) {
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/LocalContactsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/LocalContactsHelper.kt
index 0f74e15f..ce5007ea 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/LocalContactsHelper.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/LocalContactsHelper.kt
@@ -114,6 +114,7 @@ class LocalContactsHelper(val context: Context) {
contactId = localContact.id!!
thumbnailUri = ""
photo = contactPhoto
+ photoUri = localContact.photoUri
notes = localContact.notes
groups = storedGroups.filter { localContact.groups.contains(it.id) } as ArrayList
organization = Organization(localContact.company, localContact.jobPosition)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt
index 50ee8763..972ba02a 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt
@@ -2,11 +2,10 @@ package com.simplemobiletools.contacts.pro.helpers
import android.net.Uri
import android.provider.ContactsContract.CommonDataKinds
+import android.provider.ContactsContract.CommonDataKinds.*
import android.provider.MediaStore
import com.simplemobiletools.commons.activities.BaseSimpleActivity
-import com.simplemobiletools.commons.extensions.getFileOutputStream
import com.simplemobiletools.commons.extensions.showErrorToast
-import com.simplemobiletools.commons.extensions.toFileDirItem
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.contacts.pro.R
import com.simplemobiletools.contacts.pro.extensions.getByteArray
@@ -17,8 +16,12 @@ import ezvcard.Ezvcard
import ezvcard.VCard
import ezvcard.parameter.ImageType
import ezvcard.property.*
+import ezvcard.property.Email
+import ezvcard.property.Organization
+import ezvcard.property.Photo
+import ezvcard.property.StructuredName
import ezvcard.util.PartialDate
-import java.io.File
+import java.io.OutputStream
import java.util.*
class VcfExporter {
@@ -29,151 +32,149 @@ class VcfExporter {
private var contactsExported = 0
private var contactsFailed = 0
- fun exportContacts(activity: BaseSimpleActivity, file: File, contacts: ArrayList, showExportingToast: Boolean, callback: (result: ExportResult) -> Unit) {
- activity.getFileOutputStream(file.toFileDirItem(activity), true) {
- try {
- if (it == null) {
- callback(EXPORT_FAIL)
- return@getFileOutputStream
+ fun exportContacts(activity: BaseSimpleActivity, outputStream: OutputStream?, contacts: ArrayList, showExportingToast: Boolean, callback: (result: ExportResult) -> Unit) {
+ try {
+ if (outputStream == null) {
+ callback(EXPORT_FAIL)
+ return
+ }
+
+ if (showExportingToast) {
+ activity.toast(R.string.exporting)
+ }
+
+ val cards = ArrayList()
+ for (contact in contacts) {
+ val card = VCard()
+ StructuredName().apply {
+ prefixes.add(contact.prefix)
+ given = contact.firstName
+ additionalNames.add(contact.middleName)
+ family = contact.surname
+ suffixes.add(contact.suffix)
+ card.structuredName = this
}
- if (showExportingToast) {
- activity.toast(R.string.exporting)
+ if (contact.nickname.isNotEmpty()) {
+ card.setNickname(contact.nickname)
}
- val cards = ArrayList()
- for (contact in contacts) {
- val card = VCard()
- StructuredName().apply {
- prefixes.add(contact.prefix)
- given = contact.firstName
- additionalNames.add(contact.middleName)
- family = contact.surname
- suffixes.add(contact.suffix)
- card.structuredName = this
- }
+ contact.phoneNumbers.forEach {
+ val phoneNumber = Telephone(it.value)
+ phoneNumber.parameters.addType(getPhoneNumberTypeLabel(it.type, it.label))
+ card.addTelephoneNumber(phoneNumber)
+ }
- if (contact.nickname.isNotEmpty()) {
- card.setNickname(contact.nickname)
- }
+ contact.emails.forEach {
+ val email = Email(it.value)
+ email.parameters.addType(getEmailTypeLabel(it.type, it.label))
+ card.addEmail(email)
+ }
- contact.phoneNumbers.forEach {
- val phoneNumber = Telephone(it.value)
- phoneNumber.parameters.addType(getPhoneNumberTypeLabel(it.type, it.label))
- card.addTelephoneNumber(phoneNumber)
- }
-
- contact.emails.forEach {
- val email = Email(it.value)
- email.parameters.addType(getEmailTypeLabel(it.type, it.label))
- card.addEmail(email)
- }
-
- contact.events.forEach {
- if (it.type == CommonDataKinds.Event.TYPE_ANNIVERSARY || it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) {
- val dateTime = it.value.getDateTimeFromDateString()
- if (it.value.startsWith("--")) {
- val partialDate = PartialDate.builder().year(null).month(dateTime.monthOfYear).date(dateTime.dayOfMonth).build()
- if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) {
- card.birthdays.add(Birthday(partialDate))
- } else {
- card.anniversaries.add(Anniversary(partialDate))
- }
+ contact.events.forEach {
+ if (it.type == Event.TYPE_ANNIVERSARY || it.type == Event.TYPE_BIRTHDAY) {
+ val dateTime = it.value.getDateTimeFromDateString()
+ if (it.value.startsWith("--")) {
+ val partialDate = PartialDate.builder().year(null).month(dateTime.monthOfYear).date(dateTime.dayOfMonth).build()
+ if (it.type == Event.TYPE_BIRTHDAY) {
+ card.birthdays.add(Birthday(partialDate))
} else {
- Calendar.getInstance().apply {
- clear()
- set(Calendar.YEAR, dateTime.year)
- set(Calendar.MONTH, dateTime.monthOfYear - 1)
- set(Calendar.DAY_OF_MONTH, dateTime.dayOfMonth)
- if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) {
- card.birthdays.add(Birthday(time))
- } else {
- card.anniversaries.add(Anniversary(time))
- }
+ card.anniversaries.add(Anniversary(partialDate))
+ }
+ } else {
+ Calendar.getInstance().apply {
+ clear()
+ set(Calendar.YEAR, dateTime.year)
+ set(Calendar.MONTH, dateTime.monthOfYear - 1)
+ set(Calendar.DAY_OF_MONTH, dateTime.dayOfMonth)
+ if (it.type == Event.TYPE_BIRTHDAY) {
+ card.birthdays.add(Birthday(time))
+ } else {
+ card.anniversaries.add(Anniversary(time))
}
}
}
}
-
- contact.addresses.forEach {
- val address = Address()
- address.streetAddress = it.value
- address.parameters.addType(getAddressTypeLabel(it.type, it.label))
- card.addAddress(address)
- }
-
- contact.IMs.forEach {
- val impp = when (it.type) {
- CommonDataKinds.Im.PROTOCOL_AIM -> Impp.aim(it.value)
- CommonDataKinds.Im.PROTOCOL_YAHOO -> Impp.yahoo(it.value)
- CommonDataKinds.Im.PROTOCOL_MSN -> Impp.msn(it.value)
- CommonDataKinds.Im.PROTOCOL_ICQ -> Impp.icq(it.value)
- CommonDataKinds.Im.PROTOCOL_SKYPE -> Impp.skype(it.value)
- CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK -> Impp(HANGOUTS, it.value)
- CommonDataKinds.Im.PROTOCOL_QQ -> Impp(QQ, it.value)
- CommonDataKinds.Im.PROTOCOL_JABBER -> Impp(JABBER, it.value)
- else -> Impp(it.label, it.value)
- }
-
- card.addImpp(impp)
- }
-
- if (contact.notes.isNotEmpty()) {
- card.addNote(contact.notes)
- }
-
- if (contact.organization.isNotEmpty()) {
- val organization = Organization()
- organization.values.add(contact.organization.company)
- card.organization = organization
- card.titles.add(Title(contact.organization.jobPosition))
- }
-
- contact.websites.forEach {
- card.addUrl(it)
- }
-
- if (contact.thumbnailUri.isNotEmpty()) {
- val photoByteArray = MediaStore.Images.Media.getBitmap(activity.contentResolver, Uri.parse(contact.thumbnailUri)).getByteArray()
- val photo = Photo(photoByteArray, ImageType.JPEG)
- card.addPhoto(photo)
- }
-
- if (contact.groups.isNotEmpty()) {
- val groupList = Categories()
- contact.groups.forEach {
- groupList.values.add(it.title)
- }
-
- card.categories = groupList
- }
-
- cards.add(card)
- contactsExported++
}
- Ezvcard.write(cards).go(it)
- } catch (e: Exception) {
- activity.showErrorToast(e)
+ contact.addresses.forEach {
+ val address = Address()
+ address.streetAddress = it.value
+ address.parameters.addType(getAddressTypeLabel(it.type, it.label))
+ card.addAddress(address)
+ }
+
+ contact.IMs.forEach {
+ val impp = when (it.type) {
+ Im.PROTOCOL_AIM -> Impp.aim(it.value)
+ Im.PROTOCOL_YAHOO -> Impp.yahoo(it.value)
+ Im.PROTOCOL_MSN -> Impp.msn(it.value)
+ Im.PROTOCOL_ICQ -> Impp.icq(it.value)
+ Im.PROTOCOL_SKYPE -> Impp.skype(it.value)
+ Im.PROTOCOL_GOOGLE_TALK -> Impp(HANGOUTS, it.value)
+ Im.PROTOCOL_QQ -> Impp(QQ, it.value)
+ Im.PROTOCOL_JABBER -> Impp(JABBER, it.value)
+ else -> Impp(it.label, it.value)
+ }
+
+ card.addImpp(impp)
+ }
+
+ if (contact.notes.isNotEmpty()) {
+ card.addNote(contact.notes)
+ }
+
+ if (contact.organization.isNotEmpty()) {
+ val organization = Organization()
+ organization.values.add(contact.organization.company)
+ card.organization = organization
+ card.titles.add(Title(contact.organization.jobPosition))
+ }
+
+ contact.websites.forEach {
+ card.addUrl(it)
+ }
+
+ if (contact.thumbnailUri.isNotEmpty()) {
+ val photoByteArray = MediaStore.Images.Media.getBitmap(activity.contentResolver, Uri.parse(contact.thumbnailUri)).getByteArray()
+ val photo = Photo(photoByteArray, ImageType.JPEG)
+ card.addPhoto(photo)
+ }
+
+ if (contact.groups.isNotEmpty()) {
+ val groupList = Categories()
+ contact.groups.forEach {
+ groupList.values.add(it.title)
+ }
+
+ card.categories = groupList
+ }
+
+ cards.add(card)
+ contactsExported++
}
- callback(when {
- contactsExported == 0 -> EXPORT_FAIL
- contactsFailed > 0 -> ExportResult.EXPORT_PARTIAL
- else -> ExportResult.EXPORT_OK
- })
+ Ezvcard.write(cards).go(outputStream)
+ } catch (e: Exception) {
+ activity.showErrorToast(e)
}
+
+ callback(when {
+ contactsExported == 0 -> EXPORT_FAIL
+ contactsFailed > 0 -> ExportResult.EXPORT_PARTIAL
+ else -> ExportResult.EXPORT_OK
+ })
}
private fun getPhoneNumberTypeLabel(type: Int, label: String) = when (type) {
- CommonDataKinds.Phone.TYPE_MOBILE -> CELL
- CommonDataKinds.Phone.TYPE_HOME -> HOME
- CommonDataKinds.Phone.TYPE_WORK -> WORK
- CommonDataKinds.Phone.TYPE_MAIN -> PREF
- CommonDataKinds.Phone.TYPE_FAX_WORK -> WORK_FAX
- CommonDataKinds.Phone.TYPE_FAX_HOME -> HOME_FAX
- CommonDataKinds.Phone.TYPE_PAGER -> PAGER
- CommonDataKinds.Phone.TYPE_OTHER -> OTHER
+ Phone.TYPE_MOBILE -> CELL
+ Phone.TYPE_HOME -> HOME
+ Phone.TYPE_WORK -> WORK
+ Phone.TYPE_MAIN -> PREF
+ Phone.TYPE_FAX_WORK -> WORK_FAX
+ Phone.TYPE_FAX_HOME -> HOME_FAX
+ Phone.TYPE_PAGER -> PAGER
+ Phone.TYPE_OTHER -> OTHER
else -> label
}
@@ -186,9 +187,9 @@ class VcfExporter {
}
private fun getAddressTypeLabel(type: Int, label: String) = when (type) {
- CommonDataKinds.StructuredPostal.TYPE_HOME -> HOME
- CommonDataKinds.StructuredPostal.TYPE_WORK -> WORK
- CommonDataKinds.StructuredPostal.TYPE_OTHER -> OTHER
+ StructuredPostal.TYPE_HOME -> HOME
+ StructuredPostal.TYPE_WORK -> WORK
+ StructuredPostal.TYPE_OTHER -> OTHER
else -> label
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt
index a9030e19..fedde9e3 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt
@@ -3,6 +3,7 @@ package com.simplemobiletools.contacts.pro.helpers
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.provider.ContactsContract.CommonDataKinds
+import android.provider.ContactsContract.CommonDataKinds.*
import android.widget.Toast
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.contacts.pro.activities.SimpleActivity
@@ -12,6 +13,9 @@ import com.simplemobiletools.contacts.pro.extensions.groupsDB
import com.simplemobiletools.contacts.pro.extensions.normalizeNumber
import com.simplemobiletools.contacts.pro.helpers.VcfImporter.ImportResult.*
import com.simplemobiletools.contacts.pro.models.*
+import com.simplemobiletools.contacts.pro.models.Email
+import com.simplemobiletools.contacts.pro.models.Event
+import com.simplemobiletools.contacts.pro.models.Organization
import ezvcard.Ezvcard
import ezvcard.VCard
import java.io.File
@@ -50,7 +54,7 @@ class VcfImporter(val activity: SimpleActivity) {
ezContact.telephoneNumbers.forEach {
val number = it.text
val type = getPhoneNumberTypeId(it.types.firstOrNull()?.value ?: MOBILE, it.types.getOrNull(1)?.value)
- val label = if (type == CommonDataKinds.Phone.TYPE_CUSTOM) {
+ val label = if (type == Phone.TYPE_CUSTOM) {
it.types.firstOrNull()?.value ?: ""
} else {
""
@@ -76,7 +80,7 @@ class VcfImporter(val activity: SimpleActivity) {
ezContact.addresses.forEach {
val address = it.streetAddress
val type = getAddressTypeId(it.types.firstOrNull()?.value ?: HOME)
- val label = if (type == CommonDataKinds.StructuredPostal.TYPE_CUSTOM) {
+ val label = if (type == StructuredPostal.TYPE_CUSTOM) {
it.types.firstOrNull()?.value ?: ""
} else {
""
@@ -115,24 +119,24 @@ class VcfImporter(val activity: SimpleActivity) {
val typeString = it.uri.scheme
val value = URLDecoder.decode(it.uri.toString().substring(it.uri.scheme.length + 1), "UTF-8")
val type = when {
- it.isAim -> CommonDataKinds.Im.PROTOCOL_AIM
- it.isYahoo -> CommonDataKinds.Im.PROTOCOL_YAHOO
- it.isMsn -> CommonDataKinds.Im.PROTOCOL_MSN
- it.isIcq -> CommonDataKinds.Im.PROTOCOL_ICQ
- it.isSkype -> CommonDataKinds.Im.PROTOCOL_SKYPE
- typeString == HANGOUTS -> CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
- typeString == QQ -> CommonDataKinds.Im.PROTOCOL_QQ
- typeString == JABBER -> CommonDataKinds.Im.PROTOCOL_JABBER
- else -> CommonDataKinds.Im.PROTOCOL_CUSTOM
+ it.isAim -> Im.PROTOCOL_AIM
+ it.isYahoo -> Im.PROTOCOL_YAHOO
+ it.isMsn -> Im.PROTOCOL_MSN
+ it.isIcq -> Im.PROTOCOL_ICQ
+ it.isSkype -> Im.PROTOCOL_SKYPE
+ typeString == HANGOUTS -> Im.PROTOCOL_GOOGLE_TALK
+ typeString == QQ -> Im.PROTOCOL_QQ
+ typeString == JABBER -> Im.PROTOCOL_JABBER
+ else -> Im.PROTOCOL_CUSTOM
}
- val label = if (type == CommonDataKinds.Im.PROTOCOL_CUSTOM) URLDecoder.decode(typeString, "UTF-8") else ""
+ val label = if (type == Im.PROTOCOL_CUSTOM) URLDecoder.decode(typeString, "UTF-8") else ""
val IM = IM(value, type, label)
IMs.add(IM)
}
val contact = Contact(0, prefix, firstName, middleName, surname, suffix, nickname, photoUri, phoneNumbers, emails, addresses, events,
- targetContactSource, starred, contactId, thumbnailUri, photo, notes, groups, organization, websites, IMs)
+ targetContactSource, starred, contactId, thumbnailUri, photo, notes, groups, organization, websites, IMs)
// 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) {
@@ -189,28 +193,28 @@ class VcfImporter(val activity: SimpleActivity) {
}
private fun getPhoneNumberTypeId(type: String, subtype: String?) = when (type.toUpperCase()) {
- CELL -> CommonDataKinds.Phone.TYPE_MOBILE
+ CELL -> Phone.TYPE_MOBILE
HOME -> {
if (subtype?.toUpperCase() == FAX) {
- CommonDataKinds.Phone.TYPE_FAX_HOME
+ Phone.TYPE_FAX_HOME
} else {
- CommonDataKinds.Phone.TYPE_HOME
+ Phone.TYPE_HOME
}
}
WORK -> {
if (subtype?.toUpperCase() == FAX) {
- CommonDataKinds.Phone.TYPE_FAX_WORK
+ Phone.TYPE_FAX_WORK
} else {
- CommonDataKinds.Phone.TYPE_WORK
+ Phone.TYPE_WORK
}
}
- PREF, MAIN -> CommonDataKinds.Phone.TYPE_MAIN
- WORK_FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK
- HOME_FAX -> CommonDataKinds.Phone.TYPE_FAX_HOME
- FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK
- PAGER -> CommonDataKinds.Phone.TYPE_PAGER
- OTHER -> CommonDataKinds.Phone.TYPE_OTHER
- else -> CommonDataKinds.Phone.TYPE_CUSTOM
+ PREF, MAIN -> Phone.TYPE_MAIN
+ WORK_FAX -> Phone.TYPE_FAX_WORK
+ HOME_FAX -> Phone.TYPE_FAX_HOME
+ FAX -> Phone.TYPE_FAX_WORK
+ PAGER -> Phone.TYPE_PAGER
+ OTHER -> Phone.TYPE_OTHER
+ else -> Phone.TYPE_CUSTOM
}
private fun getEmailTypeId(type: String) = when (type.toUpperCase()) {
@@ -222,10 +226,10 @@ class VcfImporter(val activity: SimpleActivity) {
}
private fun getAddressTypeId(type: String) = when (type.toUpperCase()) {
- HOME -> CommonDataKinds.StructuredPostal.TYPE_HOME
- WORK -> CommonDataKinds.StructuredPostal.TYPE_WORK
- OTHER -> CommonDataKinds.StructuredPostal.TYPE_OTHER
- else -> CommonDataKinds.StructuredPostal.TYPE_CUSTOM
+ HOME -> StructuredPostal.TYPE_HOME
+ WORK -> StructuredPostal.TYPE_WORK
+ OTHER -> StructuredPostal.TYPE_OTHER
+ else -> StructuredPostal.TYPE_CUSTOM
}
private fun savePhoto(byteArray: ByteArray?): String {
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/BlockedNumber.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/BlockedNumber.kt
deleted file mode 100644
index 13fe44fd..00000000
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/BlockedNumber.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.simplemobiletools.contacts.pro.models
-
-data class BlockedNumber(val id: Long, val number: String, val normalizedNumber: String)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/CallContact.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/CallContact.kt
new file mode 100644
index 00000000..86a3ae69
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/CallContact.kt
@@ -0,0 +1,4 @@
+package com.simplemobiletools.contacts.pro.models
+
+// a simpler Contact model containing just info needed at the call screen
+data class CallContact(var name: String, var photoUri: String)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Contact.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Contact.kt
index 8533f942..cbe82e81 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Contact.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Contact.kt
@@ -138,4 +138,6 @@ data class Contact(var id: Int, var prefix: String, var firstName: String, var m
}
fun isPrivate() = source == SMT_PRIVATE
+
+ fun getSignatureKey() = if (photoUri.isNotEmpty()) photoUri else hashCode()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/LocalContact.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/LocalContact.kt
index a2947246..53f96fcd 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/LocalContact.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/LocalContact.kt
@@ -15,6 +15,7 @@ data class LocalContact(
@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,
@ColumnInfo(name = "emails") var emails: ArrayList,
@ColumnInfo(name = "events") var events: ArrayList,
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/receivers/CallActionReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/receivers/CallActionReceiver.kt
new file mode 100644
index 00000000..9ae27eb3
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/receivers/CallActionReceiver.kt
@@ -0,0 +1,17 @@
+package com.simplemobiletools.contacts.pro.receivers
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import com.simplemobiletools.contacts.pro.helpers.ACCEPT_CALL
+import com.simplemobiletools.contacts.pro.helpers.CallManager
+import com.simplemobiletools.contacts.pro.helpers.DECLINE_CALL
+
+class CallActionReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ when (intent.action) {
+ ACCEPT_CALL -> CallManager.accept()
+ DECLINE_CALL -> CallManager.reject()
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/services/CallService.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/services/CallService.kt
new file mode 100644
index 00000000..139bd208
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/services/CallService.kt
@@ -0,0 +1,25 @@
+package com.simplemobiletools.contacts.pro.services
+
+import android.content.Intent
+import android.os.Build
+import android.telecom.Call
+import android.telecom.InCallService
+import androidx.annotation.RequiresApi
+import com.simplemobiletools.contacts.pro.activities.CallActivity
+import com.simplemobiletools.contacts.pro.helpers.CallManager
+
+@RequiresApi(Build.VERSION_CODES.M)
+class CallService : InCallService() {
+ override fun onCallAdded(call: Call) {
+ super.onCallAdded(call)
+ val intent = Intent(this, CallActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(intent)
+ CallManager.call = call
+ }
+
+ override fun onCallRemoved(call: Call) {
+ super.onCallRemoved(call)
+ CallManager.call = null
+ }
+}
diff --git a/app/src/main/res/drawable/contact_circular_background.xml b/app/src/main/res/drawable/contact_circular_background.xml
deleted file mode 100644
index bd3dc800..00000000
--- a/app/src/main/res/drawable/contact_circular_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_business_vector.xml b/app/src/main/res/drawable/ic_business_vector.xml
deleted file mode 100644
index 9da2fe87..00000000
--- a/app/src/main/res/drawable/ic_business_vector.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_call_accept.xml b/app/src/main/res/drawable/ic_call_accept.xml
new file mode 100644
index 00000000..546b3ac0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_call_accept.xml
@@ -0,0 +1,16 @@
+
+
+ -
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_call_decline.xml b/app/src/main/res/drawable/ic_call_decline.xml
new file mode 100644
index 00000000..5754ba70
--- /dev/null
+++ b/app/src/main/res/drawable/ic_call_decline.xml
@@ -0,0 +1,16 @@
+
+
+ -
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_group_add_vector.xml b/app/src/main/res/drawable/ic_group_add_vector.xml
index fe6e21bb..6d4dab73 100644
--- a/app/src/main/res/drawable/ic_group_add_vector.xml
+++ b/app/src/main/res/drawable/ic_group_add_vector.xml
@@ -1,9 +1,9 @@
-
+
diff --git a/app/src/main/res/drawable/ic_group_vector.xml b/app/src/main/res/drawable/ic_group_vector.xml
deleted file mode 100644
index 120218d2..00000000
--- a/app/src/main/res/drawable/ic_group_vector.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_microphone_off_vector.xml b/app/src/main/res/drawable/ic_microphone_off_vector.xml
new file mode 100644
index 00000000..c05115e4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_microphone_off_vector.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_new_contact_vector.xml b/app/src/main/res/drawable/ic_new_contact_vector.xml
deleted file mode 100644
index b51c33ae..00000000
--- a/app/src/main/res/drawable/ic_new_contact_vector.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_phone_down_red_vector.xml b/app/src/main/res/drawable/ic_phone_down_red_vector.xml
new file mode 100644
index 00000000..763a320e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_phone_down_red_vector.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_phone_down_vector.xml b/app/src/main/res/drawable/ic_phone_down_vector.xml
new file mode 100644
index 00000000..6487b6a6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_phone_down_vector.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_phone_green_vector.xml b/app/src/main/res/drawable/ic_phone_green_vector.xml
new file mode 100644
index 00000000..312e1562
--- /dev/null
+++ b/app/src/main/res/drawable/ic_phone_green_vector.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_speaker_off_vector.xml b/app/src/main/res/drawable/ic_speaker_off_vector.xml
new file mode 100644
index 00000000..17adb3f9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_speaker_off_vector.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_speaker_on_vector.xml b/app/src/main/res/drawable/ic_speaker_on_vector.xml
new file mode 100644
index 00000000..bc7422fc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_speaker_on_vector.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ripple_background.xml b/app/src/main/res/drawable/ripple_background.xml
new file mode 100644
index 00000000..783c769d
--- /dev/null
+++ b/app/src/main/res/drawable/ripple_background.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_call.xml b/app/src/main/res/layout/activity_call.xml
new file mode 100644
index 00000000..dfd946cd
--- /dev/null
+++ b/app/src/main/res/layout/activity_call.xml
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_dialpad.xml b/app/src/main/res/layout/activity_dialpad.xml
index 8b33cc01..45da045d 100644
--- a/app/src/main/res/layout/activity_dialpad.xml
+++ b/app/src/main/res/layout/activity_dialpad.xml
@@ -1,6 +1,5 @@
-
+ app:layout_constraintTop_toTopOf="parent" />
-
+
@@ -39,7 +36,7 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/divider"
- app:layout_constraintBottom_toTopOf="@+id/dialpad_input"/>
+ app:layout_constraintBottom_toTopOf="@+id/dialpad_input" />
+ app:layout_constraintStart_toStartOf="parent" />
+ app:layout_constraintTop_toTopOf="@+id/dialpad_input" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ app:layout_constraintBottom_toTopOf="@+id/dialpad_call_button" />
+ app:layout_constraintStart_toStartOf="parent" />
diff --git a/app/src/main/res/layout/activity_edit_contact.xml b/app/src/main/res/layout/activity_edit_contact.xml
index 86758898..48e1425b 100644
--- a/app/src/main/res/layout/activity_edit_contact.xml
+++ b/app/src/main/res/layout/activity_edit_contact.xml
@@ -478,7 +478,7 @@
android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingBottom="@dimen/small_margin"
- android:src="@drawable/ic_group_vector"/>
+ android:src="@drawable/ic_people_vector"/>
-
+ android:textStyle="italic"
+ android:visibility="gone" />
+ android:visibility="gone" />
+ app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager" />
-
+
@@ -64,6 +65,6 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/activity_margin"
- android:src="@drawable/ic_plus_vector"/>
+ android:src="@drawable/ic_plus_vector" />
diff --git a/app/src/main/res/layout/activity_insert_edit_contact.xml b/app/src/main/res/layout/activity_insert_edit_contact.xml
index 2e28b29c..ba4a9e87 100644
--- a/app/src/main/res/layout/activity_insert_edit_contact.xml
+++ b/app/src/main/res/layout/activity_insert_edit_contact.xml
@@ -31,7 +31,7 @@
android:layout_height="@dimen/normal_icon_size"
android:layout_centerVertical="true"
android:padding="@dimen/medium_margin"
- android:src="@drawable/ic_new_contact_vector" />
+ android:src="@drawable/ic_add_person_vector" />
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_select_contact.xml b/app/src/main/res/layout/activity_select_contact.xml
index ea4a0dab..11d31e36 100644
--- a/app/src/main/res/layout/activity_select_contact.xml
+++ b/app/src/main/res/layout/activity_select_contact.xml
@@ -10,12 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
- android:layout_marginTop="@dimen/activity_margin"
+ android:alpha="0.8"
android:gravity="center"
android:paddingStart="@dimen/activity_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
android:text="@string/no_contacts_found"
android:textSize="@dimen/bigger_text_size"
+ android:textStyle="italic"
android:visibility="gone" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_add_blocked_number.xml b/app/src/main/res/layout/dialog_add_blocked_number.xml
deleted file mode 100644
index 7e26e1f1..00000000
--- a/app/src/main/res/layout/dialog_add_blocked_number.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/dialpad.xml b/app/src/main/res/layout/dialpad.xml
new file mode 100644
index 00000000..28561ddf
--- /dev/null
+++ b/app/src/main/res/layout/dialpad.xml
@@ -0,0 +1,268 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_layout.xml b/app/src/main/res/layout/fragment_layout.xml
index 9180dcac..0cd8f381 100644
--- a/app/src/main/res/layout/fragment_layout.xml
+++ b/app/src/main/res/layout/fragment_layout.xml
@@ -1,6 +1,5 @@
-
+ android:textStyle="italic"
+ android:visibility="gone" />
+ android:visibility="gone" />
+ app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager" />
-
+
@@ -60,6 +61,6 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/activity_margin"
- android:src="@drawable/ic_plus_vector"/>
+ android:src="@drawable/ic_plus_vector" />
diff --git a/app/src/main/res/layout/fragment_letters_layout.xml b/app/src/main/res/layout/fragment_letters_layout.xml
index 86ab1632..bea2b84c 100644
--- a/app/src/main/res/layout/fragment_letters_layout.xml
+++ b/app/src/main/res/layout/fragment_letters_layout.xml
@@ -1,6 +1,5 @@
-
+ android:textStyle="italic"
+ android:visibility="gone" />
+ android:visibility="gone" />
+ app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager" />
+ android:src="@drawable/ic_plus_vector" />
diff --git a/app/src/main/res/layout/item_add_favorite_with_number.xml b/app/src/main/res/layout/item_add_favorite_with_number.xml
index 8a1ff174..06de1793 100644
--- a/app/src/main/res/layout/item_add_favorite_with_number.xml
+++ b/app/src/main/res/layout/item_add_favorite_with_number.xml
@@ -12,16 +12,19 @@
android:id="@+id/contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/medium_margin"
- android:paddingEnd="@dimen/normal_margin"
- android:paddingBottom="@dimen/medium_margin">
+ android:minHeight="@dimen/min_row_height"
+ android:paddingStart="@dimen/tiny_margin"
+ android:paddingTop="@dimen/normal_margin"
+ android:paddingEnd="@dimen/activity_margin"
+ android:paddingBottom="@dimen/normal_margin">
@@ -47,6 +50,9 @@
android:layout_toEndOf="@+id/contact_tmb"
android:alpha="0.6"
android:maxLines="1"
+ android:ellipsize="end"
+ android:paddingStart="@dimen/medium_margin"
+ android:paddingEnd="@dimen/activity_margin"
android:textSize="@dimen/bigger_text_size"
tools:text="0123 456 789" />
diff --git a/app/src/main/res/layout/item_add_favorite_without_number.xml b/app/src/main/res/layout/item_add_favorite_without_number.xml
index 2519eb54..e562e240 100644
--- a/app/src/main/res/layout/item_add_favorite_without_number.xml
+++ b/app/src/main/res/layout/item_add_favorite_without_number.xml
@@ -12,16 +12,19 @@
android:id="@+id/contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/tiny_margin"
- android:paddingEnd="@dimen/normal_margin"
- android:paddingBottom="@dimen/tiny_margin">
+ android:minHeight="@dimen/min_row_height"
+ android:paddingStart="@dimen/tiny_margin"
+ android:paddingTop="@dimen/medium_margin"
+ android:paddingEnd="@dimen/activity_margin"
+ android:paddingBottom="@dimen/medium_margin">
diff --git a/app/src/main/res/layout/item_contact_with_number.xml b/app/src/main/res/layout/item_contact_with_number.xml
index b491454c..663ad5b7 100644
--- a/app/src/main/res/layout/item_contact_with_number.xml
+++ b/app/src/main/res/layout/item_contact_with_number.xml
@@ -13,9 +13,11 @@
android:id="@+id/contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/medium_margin"
+ android:minHeight="@dimen/min_row_height"
+ android:paddingStart="@dimen/tiny_margin"
+ android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/activity_margin"
- android:paddingBottom="@dimen/medium_margin">
+ android:paddingBottom="@dimen/normal_margin">
@@ -44,7 +48,10 @@
android:layout_alignStart="@+id/contact_name"
android:layout_toEndOf="@+id/contact_tmb"
android:alpha="0.6"
+ android:ellipsize="end"
android:maxLines="1"
+ android:paddingStart="@dimen/medium_margin"
+ android:paddingEnd="@dimen/activity_margin"
android:textSize="@dimen/big_text_size"
tools:text="0123 456 789" />
diff --git a/app/src/main/res/layout/item_contact_without_number.xml b/app/src/main/res/layout/item_contact_without_number.xml
index 9ecbc4e5..86e22567 100644
--- a/app/src/main/res/layout/item_contact_without_number.xml
+++ b/app/src/main/res/layout/item_contact_without_number.xml
@@ -13,9 +13,11 @@
android:id="@+id/contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/small_margin"
+ android:minHeight="@dimen/min_row_height"
+ android:paddingStart="@dimen/tiny_margin"
+ android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/activity_margin"
- android:paddingBottom="@dimen/small_margin">
+ android:paddingBottom="@dimen/medium_margin">
diff --git a/app/src/main/res/layout/item_group.xml b/app/src/main/res/layout/item_group.xml
index 08bb3b56..b57021ff 100644
--- a/app/src/main/res/layout/item_group.xml
+++ b/app/src/main/res/layout/item_group.xml
@@ -13,17 +13,20 @@
android:id="@+id/group_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/tiny_margin"
+ android:minHeight="@dimen/min_row_height"
+ android:paddingStart="@dimen/tiny_margin"
+ android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/activity_margin"
- android:paddingBottom="@dimen/tiny_margin">
+ android:paddingBottom="@dimen/medium_margin">
+ android:layout_marginStart="@dimen/small_margin"
+ android:padding="@dimen/tiny_margin"
+ android:src="@drawable/ic_people_vector" />
diff --git a/app/src/main/res/layout/item_manage_blocked_number.xml b/app/src/main/res/layout/item_manage_blocked_number.xml
deleted file mode 100644
index e78f8724..00000000
--- a/app/src/main/res/layout/item_manage_blocked_number.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/menu_add_blocked_number.xml b/app/src/main/res/menu/menu_add_blocked_number.xml
deleted file mode 100644
index 44ffadd7..00000000
--- a/app/src/main/res/menu/menu_add_blocked_number.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 662f7758..917d6c63 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -14,14 +14,10 @@
ارسال رسالة الى مجموعة
ارسال بريد الكتروني الى مجموعة
Call %s
- طلب الأذونات المطلوبة
إنشاء جهة إتصال
إضافة إلى جهة موجودة
- عليك أن تجعل هذا التطبيق المتصل الإفتراضي للإستفادة من الأرقام المحظورة.
- ضبط الى الافتراضي
- لم يتم العثور على جهات اتصال.
لا توجد جهات اتصال بهذا البريد الالكتروني
لا توجد جهات اتصال بهذا الرقم
@@ -62,19 +58,16 @@
محاولة تصفية جهات الاتصال المكررة
إدارة التابات المعروضة
جهات الاتصال
- المفضلة
إظهار مربع حوار تأكيد الاتصال قبل بدء مكالمة
إظهار جهات الإتصال التي لديها أرقام هواتف فقط
عرض الحروف على لوحة الاتصال
- البريد الالكتروني
المنزل
العمل
مختلف
- رقم
جوال
رئيسي
فاكس العمل
@@ -88,9 +81,6 @@
يبدو أنك لم تضف أية جهة اتصال مفضلة حتى الآن.
- اضافة مفضلة
- اضافة الى المفضلة
- ازالة من المفضلة
يجب أن تكون في شاشة التعديل لتعديل جهة اتصال
@@ -113,13 +103,14 @@
المتصل
- اتصال
- مكالمة واردة
- مكالمة واردة من …
- مكالمة جارية
- انقطع الاتصال
- رفض
- اجابة
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
البريد الالكتروني
العناوين
الأحداث (أعياد الميلاد ، الذكرى السنوية)
- ملاحظات
منظمة
المواقع الالكترونية
مجموعات
مصدر الاتصال
المراسلة الفورية (IM)
-
- إدارة الأرقام المحظورة
- لم تحظر أي شخص.
- إضافة رقم محظور
- حظر رقم
- حظر أرقام
- أرقام محظورة
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -174,10 +155,32 @@
An app for managing your contacts without ads, respecting your privacy.
- A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchronized via Google, or other accounts. You can display your favorite contacts on a separate list.
+ 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.
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.
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
+
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
Check out the full suite of Simple Tools here:
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index e8674a9f..7f6b8438 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -14,14 +14,10 @@
Grupa SMS göndər
Grupa e-poçt göndər
%s şəxsinə zng et
- Lazım olan icazələri istə
Create new contact
Add to an existing contact
- You have to make this app the default dialer app to make use of blocked numbers.
- Set as default
- No contacts found.
No contacts with emails have been found
No contacts with phone numbers have been found
@@ -62,19 +58,16 @@
Təkrarlanmış kontaktları filtrləməyə çalış
Göstərilən nişanları idarə et
Kontaktlar
- Sevimlilər
Zəngə başlamazdan əvvəl zəng təsdiq pəncərəsi göstər
Show only contacts with phone numbers
Show letters on the dialpad
- E-poçt
Ev
İş
Başqa
- Nömrə
Mobil
Əsas
İş Faksı
@@ -88,9 +81,6 @@
Görünür, hələlik heçbir sevimli kontakt əlavə etməmisiniz.
- Sevimlilər əlavə et
- Sevimlilərə əlavə et
- Sevimlilərdən sil
Kontaktı dəyişmək üçün İdarə et ekranında olmalısınız
@@ -113,13 +103,14 @@
Dialer
- Calling
- Incoming call
- Incoming call from…
- Ongoing call
- Disconnected
- Decline
- Answer
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
E-poçtlar
Ünvanlar
Hadisələr (ad günləri, il dönümləri)
- Qeydlər
Təşkilat
Vebsaytlar
Qruplar
Kontakt kökü
Instant messaging (IM)
-
- Manage blocked numbers
- You are not blocking anyone.
- Add a blocked number
- Block number
- Block numbers
- Blocked numbers
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -174,10 +155,32 @@
An app for managing your contacts without ads, respecting your privacy.
- A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchronized via Google, or other accounts. You can display your favorite contacts on a separate list.
+ 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.
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.
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
+
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
Check out the full suite of Simple Tools here:
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index f374ed15..e03a1c19 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -14,14 +14,10 @@
Poslat skupině SMS
Poslat skupině e-mail
Zavolat %s
- Vyžádat potřebná oprávnění
Vytvořit nový kontakt
Přidat k existujícímu kontaktu
- Pro použití blokování čísel musíte nastavit aplikaci jako výchozí pro správu hovorů.
- Nastavit jako výchozí
- Nenalezeny žádné kontakty.
Nenalezeny žádné kontakty s e-maily
Nenalezeny žádné kontakty s telefonními čísly
@@ -62,19 +58,16 @@
Pokusit se vyfiltrovat duplicitní kontakty
Spravovat zobrazené karty
Kontakty
- Oblíbené
Před zahájením hovoru zobrazit potvrzovací dialog
Zobrazit jen kontakty s telefonními čísly
Zobrazit na číselníku písmena
- E-mail
Domov
Práce
Jiné
- Číslo
Mobil
Hlavní
Pracovní fax
@@ -88,9 +81,6 @@
Vypadá to, že jste ještě nepřidali žádné oblíbené kontakty.
- Přidat oblíbené
- Přidat mezi oblíbené
- Odebrat z oblíbených
Pro úpravu kontaktu musíte být v Editoru kontaktu
@@ -113,13 +103,14 @@
Telefon
- Vytáčí se
- Příchozí hovor
- Příchozí hovor od…
- Probíhající hovor
- Ukončený hovor
- Odmítnout
- Přijmout
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Rychlé vytáčení
@@ -134,23 +125,13 @@
E-maily
Adresy
Události (narozeniny, výročí)
- Poznámky
Firma
Webové stránky
Skupiny
Zdroje kontaktů
Rychlé zprávy (IM)
-
- Spravovat blokovaná čísla
- Neblokujete nikoho.
- Přidat blokované číslo
- Blokovat číslo
- Blokovat čísla
- Blokovaná čísla
-
- Opravdu chcete vymazat %s?
Kontakt bude vymazán ze všech zdrojů kontaktů.
@@ -174,11 +155,33 @@
Jednoduché kontakty Pro - Rychlá správa kontaktů
- Aplikace pro správu vašich kontaktů bez reklam, respektující vaše soukromí.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Jednoduchá aplikace na vytváření nebo správu vašich kontaktů z různých zdrojů. Mohou být uloženy buď jen ve vašem zařízení, nebo je můžete i synchronizovat přes Google či jiný účet. Své oblíbené položky si můžete zobrazit ve vlastním seznamu.
+ 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.
- Můžete ji používat i na správu e-mailů a událostí kontaktů. Nabízí možnost řazení/filtrování pomocí různých parametrů, volitelné je zobrazování příjmení před jménem.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Neobsahuje žádné reklamy, ani nepotřebná oprávnění. Má plně otevřený zdrojový kód a možnost změny barev.
diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml
index 2bb5ca15..5d1eedf8 100644
--- a/app/src/main/res/values-cy/strings.xml
+++ b/app/src/main/res/values-cy/strings.xml
@@ -14,14 +14,10 @@
Anfon SMS at grŵp
Anfon ebost at grŵp
Galw %s
- Gofyn am y caniatâd sydd ei angen
Creu cyswllt newydd
Ychwanegu at gyswllt sy\'n bodoli
- Rhaid gwneud yr ap hwn yr ap deialu rhagosodedig er mwyn defnyddio rhifau wedi\'u rhwystro.
- Defnyddio fel y rhagosodedig
- Ni chanfuwyd unrhyw gysylltiadau.
Ni chanfuwyd unrhyw gysylltiadau gydag ebost
Ni chanfuwyd unrhyw gysylltiadau gyda rhifau ffôn
@@ -62,19 +58,16 @@
Ceisio canfod a gwaredu cysylltiadau dyblyg
Rheoli pa dabiau sy\'n cael eu dangos
Cysylltiadau
- Ffefrynnau
Dangos deialog i gadarnhau cyn gwneud galwad
Dangos dim ond cysylltiadau gyda rhifau ffôn
Dangos llythrennau ar y pad deialu
- Ebost
Cartref
Gwaith
Arall
- Rhif
Symudol
Prif
Ffacs gwaith
@@ -88,9 +81,6 @@
Ymddangosir nad wyt wedi ychwanegu unrhyw ffefrynnau eto.
- Ychwanegu ffefrynnau
- Ychwanegu i\'r ffefrynnau
- Tynnu o\'r ffefrynnau
Rhaid bod ar y sgrin golygu er mwyn addasu cyswllt
@@ -113,13 +103,14 @@
Deialydd
- Yn galw
- Galwad i mewn
- Galwad i mewn oddi wrth…
- Galwad ar y gweill
- Datgysylltwyd
- Gwrthod
- Ateb
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Cyfeiriadau ebost
Cyfeiriadau
Digwyddiadau (e.e. pen-blwyddi)
- Nodiadau
Sefydliad
Gwefannau
Grwpiau
Ffynhonnell gyswllt
Negesu ar unwaith (IM)
-
- Rheoli rhifau wedi\'u rhwystro
- Nid wyt yn rhwystro unrhyw un.
- Ychwanegu rif i\'w rwystro
- Rhwystro rhif
- Rhwystro rhifau
- Rhifau wedi\'u rhwystro
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Ap i reoli dy gysylltiadau, heb hysbysebion, gan barchu dy breifatrwydd.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Ap syml i greu neu reoli dy gysylltiadau o unrhyw ffynhonnell. Caiff y cysylltiadau eu cadw ar dy ddyfais yn unig, ond mae hefyd yn bosib eu cysoni gyda Google neu gyfrifon eraill. Gellir golygu dy hoff gysylltiadau mewn rhestr ar wahân.
+ 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.
- Gellir ei ddefnyddio i reoli cyfeiriadau e-bost defnyddwyr a digwyddiadau hefyd. Mae ganddo\'r gallu i drefnu mewn gwahanol ffyrdd ac i ddangos enwau gyda\'r cyfenw gyntaf os yw\'n well gennyt.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Does dim hysbysebion na dim angen arno unrhyw ganiatâd diangen. Mae\'n gwbl cod agored ac mae modd addasu lliwiau\'r ap.
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 71c57757..66768e8e 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -14,14 +14,10 @@
Send SMS til gruppe
Send email til gruppe
Ring til %s
- Anmod om de krævede tilladelser
Opret ny kontakt
Tilføj til en eksisterende kontakt
- Du skal gøre denne app til standardopkaldsappen for at gøre brug af blokerede numre.
- Gør til standard
- Ingen kontakter fundet.
Ingen kontakter med emails fundet
Ingen kontakter med telefonnumre fundet
@@ -62,19 +58,16 @@
Prøv at filtrere dupletter
Administrer viste faner
Kontakter
- Favoritter
Vis en opkaldsbekræftelsesdialog før du starter et opkald
Vis kun kontakter med telefonnumre
Vis bogstaver på tastaturet
- Email
Hjem
Arbejde
Andet
- Nummer
Mobil
Hovednummer
Arbejdsfax
@@ -88,9 +81,6 @@
Det ser ud til, at du ikke har tilføjet nogen favoritkontakter endnu.
- Tilføj favoritter
- Tilføj til favoritter
- Fjern fra favoritter
Du skal være på skærmen Rediger for at ændre en kontakt
@@ -113,13 +103,14 @@
Dialer
- Ringer
- Indkommende opkald call
- Indkommende opkald fra…
- Igangværende opkald
- Afbrudt
- Afvis
- Besvar
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Emails
Adresser
Begivenheder (fødselsdage, årsdage)
- Noter
Organisation
Hjemmesider
Groupper
Kontaktkilde
Instant messaging (IM)
-
- Administrér blokerede numre
- Du blokerer ikke nogen
- Tilføj et blokeret nummer
- Blokér nummer
- Blokér numre
- Blokerede numre
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- En app til dine kontakter, uden annoncer og med respekt for dit privatliv.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- En simpel app til at oprette eller administrere dine kontakter fra enhver kilde. Kontakterne kan gemmes kun på din enhed, eller synkroniseres via Google eller andre konti. Du kan vise dine foretrukne kontakter på en separat liste.
+ 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.
- Du kan også bruge den til at styre bruger-emails og begivenheder. Den har muligheden for at sortere / filtrere efter flere parametre, og vise efternavn eller fornavn først.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Indeholder ingen annoncer eller unødvendige tilladelser. Det er fuldstændigt opensource. Appens farver kan tilpasses.
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index fc265c69..c41b99d1 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -14,14 +14,10 @@
SMS an Gruppe senden
E-Mail an Gruppe senden
%s anrufen
- Benötigte Berechtigungen anfordern
Neuen Kontakt erstellen
Zu einem existierenden Kontakt hinzufügen
- Du musst diese App als Standardtelefonie-App einstellen, um Nummern blockieren zu können.
- Als Standard auswählen
- Keine Kontakte gefunden.
Keine Kontakte mit E-Mailadressen gefunden
Keine Kontakte mit Telefonnummern gefunden
@@ -62,19 +58,16 @@
Versucht Kontaktduplikate herauszufiltern
Anzuzeigende Tabs festlegen
Kontakte
- Favoriten
Bestätigungsdialog zeigen, bevor ein Anruf durchgeführt wird
Nur Kontakte mit Telefonnummern anzeigen
Buchstaben im Wahlfeld anzeigen
- E-Mail
Privat
Arbeit
Sonstiges
- Nummer
Mobil
Festnetz
Arbeit Fax
@@ -88,9 +81,6 @@
Anscheinend haben Sie bisher keine Kontakte zu den Favoriten hinzugefügt.
- Favoriten hinzufügen
- Zu Favoriten hinzufügen
- Aus Favoriten entfernen
Sie müssen sich im Bearbeitungsmodus befinden, um einen Kontakt zu bearbeiten.
@@ -113,13 +103,14 @@
Dialer
- Anrufaufbau
- Eingehender Anruf
- Eingehender Anruf von…
- Laufender Anruf
- Getrennt
- Ablehnen
- Antworten
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
E-Mails
Adressen
Termine (Geburtstage, Jahrestage)
- Notizen
Organisation
Webseiten
Gruppen
Kontaktquelle
Instant messaging (IM)
-
- Blockierte Nummern verwalten
- Du blockierst niemanden.
- Eine blockierte Nummer hinzufügen
- Nummer blockieren
- Nummern blockieren
- Blockierte Nummern
-
- Sind Sie sicher, dass Sie %s löschen wollen?
Der Kontakt wird von allen Kontaktquellen entfernt.
@@ -172,11 +153,33 @@
Schlichte Kontakte Pro - Mühlose Kontaktverwaltung
- Verwaltet Ihre Kontakte ohne Werbeanzeigen und respektiert Ihre Privatsphäre.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Eine einfache App, mit der Sie Kontakte erstellen oder von jeder Quelle verwalten können. Die Kontakte können entweder nur auf Ihrem Gerät gespeichert, oder mittels Google oder anderer Konten synchronisiert werden. Sie können Ihre Lieblingskontake in einer separaten Liste anzeigen.
+ 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.
- Sie können es auch zur Verwaltung von Nutzer-E-Mails und Ereignisse nutzen. Es kann nach mehreren Parametern sortieren oder filtern, oder optional den Nachnamen zuerst anzeigen.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Enthält keine Werbung oder unnötige Berechtigungen. Vollständig quelloffen. Die Farben sind anpassbar.
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 25b87a6d..f79ca9e3 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -14,14 +14,10 @@
Αποστολή SMS σε ομάδες
Αποστολή email σε ομάδες
Κλήση %s
- Ζητούνται τα απαιτούμενα δικαιώματα
Δημιουργία νέας Επαφής
Προσθήκη σε μια υπάρχουσα Επαφή
- Θα πρέπει να οριστεί προεπιλεγμένη εφαρμογή για χρησιμοποίηση αποκλεισμένων αριθμών.
- Ορισμός ως προεπιλογής
- Δεν βρέθηκαν Επαφές.
Δεν βρέθηκαν Επαφές με emails
Δεν βρέθηκαν Επαφές με αριθμό τηλεφώνου
@@ -38,7 +34,7 @@
Δεν υπάρχουν ομάδες
Δημιουργία νέας ομάδας
Αφαίρεση από ομάδα
- Η ομάδα είναι άδεια
+ Η ομάδα είναι κενή
Προσθήκη επαφών
Δεν υπάρχουν ομάδες επαφών στη συσκευή
Δημιουργία ομάδας
@@ -59,22 +55,19 @@
Κλήση επαφής
Εμφάνιση λεπτομερειών επαφής
Διαχείριση εμφανιζόμενων πεδίων επαφής
- Δοκιμάστε το φιλτράρισμα διπλών επαφών
+ Δοκιμάστε το φιλτράρισμα διπλότυπων επαφών
Διαχείριση εμφανιζόμενων καρτελών
Επαφές
- Αγαπημένες
Εμφάνιση διαλόγου επιβεβαίωσης πριν από την έναρξη μιας κλήσης
Προβολή όλων των Επαφών με αριθμούς τηλεφώνου
Εμφάνιση γραμμάτων στην πληκτρολόγιο
- Email
Οικία
Εργασία
Άλλο
- Αριθμός
Κινητό
Κύριο
Φαξ Εργασίας
@@ -88,9 +81,6 @@
Φαίνεται ότι δεν έχετε προσθέσει αγαπημένες επαφές ακόμη.
- Προσθήκη αγαπημένων
- Προσθήκη στις αγαπημένες
- Αφαίρεση από τα αγαπημένα
Πρέπει να είστε στην οθόνη "Επεξεργασία" για να τροποποιήσετε μια επαφή
@@ -103,7 +93,7 @@
Εξαγωγή επαφών
Εισαγωγή επαφών από .vcf αρχείο
Εξαγωγή επαφών σε .vcf αρχείο
- Πηγή επαφής προορισμού
+ Προορισμός πηγής επαφής
Συμπερίληψη πηγών επαφών
Όνομα αρχείου (χωρίς .vcf)
@@ -113,13 +103,14 @@
Πληκτρολόγιο
- Κλήση
- Εισερχόμενη κλήση
- Εισερχόμενη κλήση απο…
- Εξερχόμενη κλήση
- Αποσύνδεση
- Άρνηση
- Απάντηση
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Ταχεία Κλήση
@@ -134,23 +125,13 @@
Emails
Διευθύνσεις
Εκδηλώσεις (γενέθλια, επετείους)
- Σημειώσεις
Εταιρεία
Ιστοσελίδα
Ομάδες
Προέλευση επαφής
Αμεσο μήνυμα (IM)
-
- Διαχείρηση αποκλεισμένων αριθμών
- Χωρίς αποκλεισμο κανενός.
- Προσθήκη ένος αποκλεισμένου αριθμού
- Αποκλεισμό αριθμού
- Αποκλεισμό αριθμών
- Αποκλεισμένοι αριθμοί
-
- Θέλετε σίγουρα να διαγράψετε %s?
Η επαφή θα καταργηθεί από όλες τις πηγές επαφών.
@@ -172,11 +153,33 @@
Απλές Επαφές Pro - Εύκολη διαχείριση Επαφών
- Μια εφαρμογή για τις Επαφές σας χωρίς διαφημίσεις, σεβόμενη το απόρρητό σας.
+ Εύκολη και γρήγορη διαχείριση ομάδων & αγαπημένων επαφών χωρίς διαφημίσεις.
- Μια απλή εφαρμογή για δημιουργία και διαχείριση των επαφών σου από κάθε πηγή. Οι επαφές μπορεί να είναι αποθηκευμένες μόνο στη συσκευή σου, αλλά μπορούν να συγχρονίζονται στο Google, ή σε κάποιο άλλο λογαριασμό. Μπορείς να εμφανίσεις τις αγαπημένες σου επαφές σε ξεχωριστή λίστα.
+ Μια ελαφριά εφαρμογή διαχείρισης των επαφών σας που αγαπήθηκε από εκατομμύρια χρήστες. Οι επαφές μπορούν να αποθηκευτούν μόνο στη συσκευή σας, αλλά και να συγχρονιστούν μέσω της Google ή άλλων λογαριασμών.
- Μπορείτε να τη χρησιμοποιήσετε για τη διαχείριση των email και εκδηλώσεων επίσης. Έχει τη δυνατότητα ταξινόμησης/φιλτραρίσματος με διάφορες παραμέτρους, προαιρετικά να εμφανίζεται το επώνυμο πρώτα ή το όνομα.
+ Μπορείτε να την χρησιμοποιήσετε και για τη διαχείριση email και συμβάντων χρήστη. Έχει τη δυνατότητα ταξινόμισης/φιλτραρίσματος με πολλές παραμέτρους, προαιρετικά εμφανίζει το επώνυμο ως πρώτο όνομα.
+
+ Μπορείτε να εμφανίσετε τις αγαπημένες σας επαφές ή ομάδες σε ξεχωριστή λίστα. Οι ομάδες μπορούν να χρησιμοποιηθούν για την αποστολή ομαδικών μηνυμάτων email ή SMS, για να εξοικονομήσετε χρόνο, καθώς & για εύκολη μετονομασία.
+
+ Περιέχει εύχρηστα πλήκτρα στις κλήσεις ή προς αποστολή μηνυμάτων στις επαφές σας. Όλα τα ορατά πεδία μπορούν να προσαρμοστούν όπως επιθυμείτε, μπορείτε εύκολα να αποκρύψετε τα μη χρησιμοποιούμενα. Η λειτουργία Αναζήτηση θα βρεί την συμβολοσειρά σε κάθε ορατό πεδίο επαφής για να σας βοηθήσει στον εύκολο εντοπισμό της επιθυμητής επαφής.
+
+ Υπάρχει και ένα ελαφρύ πληκτρολόγιο κλήσης στην διάθεσή σας, με προτάσεις έξυπνων επαφών.
+
+ Υποστηρίζει την εξαγωγή/εισαγωγή επαφών σε μορφή vCard σε αρχεία .vcf, για εύκολη μεταφορά ή δημιουργία αντιγράφων ασφαλείας των δεδομένων σας.
+
+ Με αυτόν τον σύγχρονο & σταθερό διαχειριστή επαφών μπορείτε να προστατέψετε τις επαφές σας χωρίς να τις μοιραστείτε με άλλες εφαρμογές, ώστε να διατηρήσετε τις επαφές σας ιδιωτικές.
+
+ Όπως και στη πηγή επαφών, μπορείτε επίσης να αλλάξετε εύκολα το όνομα της επαφής, το email, τον αριθμό τηλεφώνου, τη διεύθυνση, τον οργανισμό, τις ομάδες και πολλά άλλα προσαρμόσιμα πεδία. Μπορείτε να την χρησιμοποιήσετε και για την αποθήκευση συμβάντων επαφής, όπως γενέθλια, επετείους ή άλλα προσαρμόσιμα.
+
+ Αυτό το απλό πρόγραμμα επεξεργασίας επαφών έχει πολλές εύχρηστες ρυθμίσεις, όπως εμφάνιση αριθμών τηλεφώνου στην κύρια οθόνη, εναλλαγή προβολής μικρογραφιών επαφών, εμφάνιση μόνο επαφών με αριθμούς τηλεφώνου, εμφάνιση διαλόγου επιβεβαίωσης κλήσης πριν την εκκίνηση μιας κλήσης. Έρχεται επίσης με Ταχεία κλήση χρησιμοποιώντας γράμματα.
+
+ Για περαιτέρω βελτίωση της εμπειρίας χρήστη, μπορείτε να προσαρμόσετε τις ενέργειες που θα γίνονται κάνοντας κλικ σε μια επαφή. Μπορείτε είτε να ξεκινήσετε μια κλήση, να μεταβείτε στην οθόνη, Προβολή λεπτομερειών, ή να επεξεργαστείτε την επιλεγμένη επαφή.
+
+ Μπορείτε εύκολα να αποκλείσετε αριθμούς τηλεφώνου για να αποφύγετε ανεπιθύμητες εισερχόμενες κλήσεις.
+
+ Για αποφυγή προβολής πιθανών ανεπιθύμητων επαφών, διαθέτει μια ισχυρή ενσωματωμένη συγχώνευση διπλότυπων επαφών.
+
+ Διατίθεται με σχεδίαση υλικού με σκούρο θέμα από προεπιλογή, παρέχει εξαιρετική εμπειρία χρήστη για εύκολη χρήση. Η έλλειψη πρόσβασης στο διαδίκτυο σας παρέχει περισσότερο απόρρητο, ασφάλεια και σταθερότητα από ότι άλλες εφαρμογές.
Δεν περιέχει διαφημίσεις ή περιττές άδειες. Έιναι όλη ανοιχτού κώδικα και παρέχει προσαρμόσιμα χρώματα για την εφαρμογή.
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 7bcd81ab..d5d50c0a 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -2,8 +2,8 @@
Simple Contacts
Contactos
Dirección
- Insertando...
- Actualizando...
+ Insertando…
+ Actualizando…
Almacenamiento del teléfono
Almacenamiento del teléfono (No visible para otras aplicaciones)
Compañía
@@ -14,14 +14,10 @@
Enviar SMS a grupo
Enviar correo electrónico a grupo
Llamar a %s
- Solicitar los permisos requeridos
Crear nuevo contacto
Añadir a un contacto existente
- Tienes que hacer esta aplicación la aplicación de marcación por defecto para hacer uso de los números bloqueados.
- Establecer como por defecto
- No se encontraron contactos.
No se encontraron contactos con correo electrónico
No se encontraron contactos con número de teléfono
@@ -62,19 +58,16 @@
Intentar foltrar contactos duplicados
Administrar pestañas mostradas
Contactos
- Favoritos
Mostrar un cuadro de confirmación antes de iniciar una llamada
Solo mostrar contactos con números telefónicos
Mostrar letras en el teclado de marcación
- Correo electrónico
Casa
Trabajo
Otros
- Número
Celular
Principal
Fax de trabajo
@@ -88,9 +81,6 @@
Parece que aún no has añadido ningún contacto favorito.
- Añadir favoritos
- Añadir a favoritos
- Eliminar de favoritos
Debes estar en la Pantalla de Edición para modificar un contacto
@@ -113,13 +103,14 @@
Marcador
- Llamando
- Llamada entrante
- Llamada entrante desde…
- Llamada en curso
- Desconectado
- Rechazar
- Contestar
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Correos electrónicos
Direcciones
Eventos (Cumpleaños, aniversarios)
- Notas
Organización
Sitios web
Grupos
Orígenes de contacto
Mensaje instantáneo (IM)
-
- Administrar números bloqueados
- Aún no has bloqueado a nadie.
- Añadir un número bloqueado
- Bloquear número
- Bloquear números
- Números bloqueados
-
- ¿Estás seguro que quieres eliminar %s?
El contacto será eliminado de todos los orígenes de contactos.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Administra tus contactos
- Una app para administrar tus contactos sin anuncios, respetando tu privacidad.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Una aplicación simple para crear o administrar tus contactos desde cualquier origen. Los contactos pueden ser almacenados solo en tu dispositivo, o también sincronizarlos vía Google u otras cuentas.Puedes mostrar tus contactos favoritos en una lista separada.
+ 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.
- Puedes usarla para administrar correos electrónicos y eventos. Tiene la capacidad de ordenar/filtrar por multiples parámetros, opcionalmente, mostrar apellido como el primer nombre.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
No contiene anuncios o permisos innecesarios. Es complétamente de código abierto, provee colores personalziables.
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index e2673a71..3db5c68b 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -14,14 +14,10 @@
Bidali SMSa taldera
Bidali emaila taldeari
%s deitu
- Eskatu beharrezko baimenak
Create new contact
Add to an existing contact
- You have to make this app the default dialer app to make use of blocked numbers.
- Set as default
- No contacts found.
No contacts with emails have been found
No contacts with phone numbers have been found
@@ -62,19 +58,16 @@
Saiatu bikoiztutako kontaktuak iragazten
Kudeatu erakutsitako fitxak
Kontaktuak
- Gogokoak
Erakutsi egiaztatze mezua dei bat hasi baino lehen
Show only contacts with phone numbers
Show letters on the dialpad
- Emaila
Etxea
Lana
Besterik
- Zenbakia
Mugikorra
Nagusia
Laneko faxa
@@ -88,9 +81,6 @@
Ez duzu oraindik gogokorik gehitu.
- Gehitu gogokoak
- Gehitu gogokoen zerrendara
- Kendu gogokoenetik
Kontaktu bat aldatzeko edizio pantailan egon behar zara
@@ -113,13 +103,14 @@
Dialer
- Calling
- Incoming call
- Incoming call from…
- Ongoing call
- Disconnected
- Decline
- Answer
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Emailak
Helbideak
Ekitaldiak (urtebetetzeak, urteurrenak)
- Oharrak
Erakundea
Webguneak
Taldeak
Kontaktu jatorria
Instant messaging (IM)
-
- Manage blocked numbers
- You are not blocking anyone.
- Add a blocked number
- Block number
- Block numbers
- Blocked numbers
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -174,9 +155,31 @@
An app for managing your contacts without ads, respecting your privacy.
- Aplikazio sinplea kontaktuak sortu eta kudeatzeko. Kontaktuak zure gailuan baino esin dira gorde, baina sinkronizagarriak dira Google-n edo beste kontuen bitartez. Zure kontaktu gogokoenak zerrenda banandu batean erakutsi ditzakezu.
-
- Erabiltzaileen emailak eta ekitaldiak kudeatzeko erabili dezakezu ere. Aukera duzu parametro askoren arabera sailkatzeko, tartean abizena izen gisa erakustea.
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Ez ditu iragarkirik ezta beharrezkoak ez diren baimenak. Guztiz kode irekikoa da eta koloreak pertsonalizagarriak dira. Aplikazio hau sorta handiago bateko zati bat baino ez da.
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 92f8f1a2..7ef62ebb 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -14,14 +14,10 @@
Lähetä tekstiviesti ryhmälle
Lähetä sähköposti ryhmälle
Soita
- Pyydä tarvittavia oikeuksia
Luo uusi kontakti
Lisää olemassa olevaan kontaktiin
- Soittajan täytyy olla oletus
- Aseta oletukseksi
- Kontakteja ei löytynyt.
Sähköpostillisia kontakteja ei löytynyt
Puhelinnumerollisia kontakteja ei löytynyt
@@ -62,19 +58,16 @@
Suodata kaksoiskappaleet
Muuta näytettyjä palkkeja
Kontaktit
- Suosikit
Näytä puhelun vahvistusruutu
Näytä ainoastaan numerolliset kontaktit
Näytä kirjaimet puhelimessa
- Sähköposti
Koti
Työ
Muu
- Numero
Mobiili
Päänumero
Työ faxi
@@ -88,9 +81,6 @@
Ei suosikkeja
- Lisää suosikkeja
- Lisää suosikkeihin
- Poista suosikeista
Täytyy olla muokkauksessa
@@ -113,13 +103,14 @@
Vastaaja
- Soitaa
- Tuleva puhelu
- Tuleva puhelu:
- Puhelu kesken
- Katkaistu
- Hylkää puhelu
- Vastaa puheluun
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Sähköpostit
Osoitteet
Tapahtumat
- Muistiinpanot
Järjestö
Internetsivut
Ryhmät
Kontaktin lähde
Pikaviestin
-
- Muuta estettyjä numeroita
- Et estä ketään.
- Lisää estetty numero
- Estä numero
- Estä numeroja
- Estetyt numerot
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,12 +153,34 @@
Simple Contacts Pro - Manage your contacts easily
- An app for managing your contacts without ads, respecting your privacy.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchronized via Google, or other accounts. You can display your favorite contacts on a separate list.
+ 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.
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.
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
+
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
Check out the full suite of Simple Tools here:
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 17e49c91..5c7cd66d 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -14,14 +14,10 @@
Envoyer un SMS au groupe
Envoyer un courriel au groupe
Appeler %s
- Demander les autorisations nécessaires
Créer un nouveau contact
Ajouter à un contact existant
- You have to make this app the default dialer app to make use of blocked numbers.
- Set as default
- Aucun contact n\'a été trouvé.
Aucun contact avec une adresse de courriel n\'a été trouvé
Aucun contact avec un numéro de téléphone n\'a été trouvé
@@ -62,19 +58,16 @@
Essayez de filtrer les contacts en double
Gérer les onglets affichés
Contacts
- Favoris
Afficher une demande de confirmation avant de démarrer un appel
Afficher uniquement les contacts avec un numéro de téléphone
Show letters on the dialpad
- Adresse de courriel
Maison
Travail
Autre
- Numéro
Mobile
Principal
Fax travail
@@ -88,9 +81,6 @@
Aucun contact favori n\'a été trouvé
- Ajouter des favoris
- Ajouter aux favoris
- Supprimer des favoris
Vous devez être sur l\'écran \"Modifier\" pour modifier un contact
@@ -113,13 +103,14 @@
Numéroteur
- Appel en cours
- Appel entrant
- Appel entrant de…
- Appel en cours
- Interrompu
- Refuser
- Répondre
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Adresse de courriels
Adresses
Évènements (naissances, anniversaires)
- Notes
Organisation
Sites Internet
Groupe
Compte pour le contact
Messagerie instantanée (MI)
-
- Manage blocked numbers
- You are not blocking anyone.
- Ajouter un numéro bloqué
- Numéro bloquer
- Numéros bloquer
- Numéros bloquer
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- An app for managing your contacts without ads, respecting your privacy.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Un outil simple pour créer et gérer vos contacts depuis n\'importe quelle source. Les contacts peuvent être stockés sur votre appareil mais aussi synchronisés via votre compte Google ou d\'autres comptes. Vous pouvez afficher vos contacts favoris dans une liste séparée.
+ 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.
- Vous pouvez l\'utiliser pour gérer les adresses de courriels et les événements de vos contacts. Cet outil permet de trier/filtrer à l\'aide de multiples paramètres, par exemple : afficher le surnom en premier.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
L\'application ne contient ni publicité, ni autorisation inutile. Elle est totalement opensource et est également fournie avec des couleurs personnalisables.
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index d68eda78..a20c0d29 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -14,14 +14,10 @@
Pošalji SMS grupi
Pošalji e-poštu grupi
Nazovi %s
- Zatraži potrebna dopuštenja
Stvori novi kontakt
Dodaj postojećem kontaktu
- Morate napraviti ovu aplikaciju zadanom aplikacijom za biranje da biste bili u mogućnosti koristiti blokirane brojeve.
- Postavi na zadano
- Nisu pronađeni kontakti.
Nije pronađen nijedan kontakt s e-poštom
Nisu pronađeni kontakti s telefonskim brojevima
@@ -62,19 +58,16 @@
Pokušaj filtrirati duple kontakte
Upravljaj prikazanim karticama
Kontakti
- Favoriti
Pokažite dijaloški okvir za potvrdu poziva prije pokretanja poziva
Prikaži samo kontakte s telefonskim brojevima
Prikaži slova na telefonskoj tipkovnici
- E-pošta
Kućni
Posao
Ostalo
- Broj
Mobilni
Glavni
Poslovni fax
@@ -88,9 +81,6 @@
Čini se da još niste dodali nijedan kontakt u favorite.
- Dodaj favorite
- Dodaj u favorite
- Ukloni iz favorita
Morate biti na zaslonu Uređivanje da biste izmijenili kontakt
@@ -113,13 +103,14 @@
Birač broja
- Pozivanje
- Dolazni poziv
- Dolazni poziv od…
- Poziv u tijeku
- Prekinuto
- Odbij
- Odgovori
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
E-pošte
Adrese
Događaji (rođendani, godišnjice)
- Bilješke
Organizacije
Web stranice
Grupe
Izvori kontakata
Brzo slanje poruka (IM)
-
- Upravljanje blokiranim brojevima
- Ne blokirate nikoga.
- Dodaj blokirani broj
- Blokiraj broj
- Blokiraj brojeve
- Blokirani brojevi
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Aplikacija za upravljanje kontaktima, bez oglasa, poštujući Vašu privatnost.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Jednostavna aplikacija za izradu ili upravljanje kontaktima iz bilo kojeg izvora. Kontakti mogu biti pohranjeni samo na Vašem uređaju, ali i sinkronizirani putem Googlea ili drugih računa. Možete prikazati svoje omiljene kontakte na zasebnom popisu.
+ 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.
- Možete ju koristiti za upravljanje e-poštom i događajima. Ima mogućnost sortirati/filtrirati višestrukim parametrima, po želji može prikazati prezime prvo, zatim ime.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Ne sadrži oglase ili nepotrebne dozvole. Aplikacije je otvorenog koda, pruža prilagodljive boje.
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index fd0eda71..419d27b2 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -14,14 +14,10 @@
SMS küldése csoportnak
Email küldése csoportnak
%s hívása
- A kívánt jogosultságok igénylése
Új névjegy hozzáadása
Hozzáadás meglévő névjegyhez
- A zárolt telefonszámok használatához be kell állítani, hogy ez az app legyen az alapértelmezett tárcsázó.
- Alapértelmezés beállítása
- Nincs ilyen névjegy.
Nincs emailt tartalmazó névjegy.
Nincs telefonszámot tartalmazó névjegy.
@@ -62,19 +58,16 @@
Többszörösen felvett névjegyek kiszűrése
A megjelenő fülek kiválasztása
Névjegyek
- Kedvencek
Jóváhagyás kérése telefonhívás indítása előtt
Csak telefonszámot tartalmazó névjegyek kijelzése
Betűk kijelzése a tárcsázón
- Email
Otthon
Munkahely
Egyéb
- Telefonszám
Mobil
Elsődleges telefonszám
Munkahelyi fax
@@ -88,9 +81,6 @@
Úgy tűnik, hogy még nincsenek kedvencek felvéve.
- Kedvencek felvétele
- Hozzáadás a kedvencekhez
- Törlés a kedvencek közül
Névjegyet csak a szerkesztő képernyőn lehet módosítani.
@@ -113,13 +103,14 @@
Tárcsázó
- Hívás
- Bejövő hívás
- Bejövő hívás innen…
- Hívás folyamatban
- Szétkapcsolt
- Elutasítva
- Hívásfogadás
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Emailek
Címek
Események (születésnapok, évfordulók)
- Jegyzetek
Szervezet
Weboldalak
Csoportok
Névjegy account
Instant messaging (IM)
-
- Zárolt telefonszámok kezelése
- Nincs még zárolt szám.
- Zárolt telefonszám hozzáadása
- Telefonszám zárolása
- Telefonszámok zárolása
- Zárolt telefonszámok
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Egy reklámmentes app névjegyek kezelésére, amelyik óvja a magánszférádat.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Egy egyszerű app, amelyik az összes accountod névjegyeit képes kezelni. Bár a névjegyeket csak az eszközön tárolja, de szinkronizálható Google, vagy más accountokkal is. A kedvenceidet külön listán is kezelheted.
+ 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.
- Kezelhetsz vele email-címeket és eseményeket. Képes a névjegyeket többféle paraméter rendezni és szűrni. A megjelenítést beállíthatod vezetéknév, keresztnév sorrendben is. A színeket testreszabhtod.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Nem kér szükségtelen jogosultságokat és nincs benne reklám. Teljes egészében nyílt forráskódú szoftver.
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index 7e74b651..ac3115ff 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -14,14 +14,10 @@
Kirim SMS ke grup
Kirim surel ke grup
Panggil %s
- Meminta izin yang diperlukan
Buat kontak baru
Tambah ke kontak yang ada
- Anda harus mengatur aplikasi ini sebagai aplikasi dialer default untuk menggunakan fitur pemblokir nomor.
- Tetapkan sebagai default
- Tidak ada kontak yang ditemukan.
Tidak ada kontak dengan alamat surel yang ditemukan
Tidak ada kontak dengan nomor telepon yang ditemukan
@@ -62,19 +58,16 @@
Coba sembunyikan duplikat kontak
Kelola tab yang ditampilkan
Kontak
- Favorit
Tampilkan dialog konfirmasi panggilan sebelum melakukan panggilan
Hanya tampilkan kontak dengan nomor telepon
Tampilkan huruf pada tombol dial
- Surel
Rumah
Kerja
Lainnya
- Nomor
Ponsel
Utama
Faks Kerja
@@ -88,9 +81,6 @@
Sepertinya anda belum menambahkan kontak favorit.
- Tambah favorit
- Tambah ke favorit
- Buang dari favorit
Anda harus berada di layar Sunting untuk mengubah kontak
@@ -113,13 +103,14 @@
Telepon
- Memanggil
- Panggilan masuk
- Panggilan masuk dari…
- Panggilan keluar
- Terputus
- Tolak
- Jawab
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Surel
Alamat
Acara (ulang tahun, hari jadi)
- Catatan
Organisasi
Situs web
Grup
Sumber kontak
Pesan singkat (IM)
-
- Kelola nomor yang diblokir
- Anda tidak memblokir siapapun.
- Tambahkan nomor yang diblokir
- Blokir nomor
- Blokir nomor
- Nomor yang diblokir
-
- Apakah anda yakin ingin menghapus %s?
Kontak akan dihapus dari semua sumber kontak.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Aplikasi untuk mengelola kontak tanpa iklan, menghargai privasi anda.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Aplikasi sederhana untuk membuat dan mengelola kontak anda dari berbagai sumber. Kontak bisa disimpan hanya pada perangkat anda, tetapi juga disinkronisasikan melalui Google, atau akun lainnya. Anda bisa menampilkan kontak favorit anda pada daftar terpisah.
+ 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.
- Anda juga bisa menggunakannya untuk mengelola surel dan acara. Memiliki kemampuan untuk mengurutkan/menyaring menggunakan banyak parameter, dan secara opsional menampilkan nama belakang sebagai awalan nama.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Tanpa iklan dan perizinan yang tidak perlu. Sepenuhnya sumber terbuka, dengan warna yang bisa disesuaikan.
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 7e74b651..ac3115ff 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -14,14 +14,10 @@
Kirim SMS ke grup
Kirim surel ke grup
Panggil %s
- Meminta izin yang diperlukan
Buat kontak baru
Tambah ke kontak yang ada
- Anda harus mengatur aplikasi ini sebagai aplikasi dialer default untuk menggunakan fitur pemblokir nomor.
- Tetapkan sebagai default
- Tidak ada kontak yang ditemukan.
Tidak ada kontak dengan alamat surel yang ditemukan
Tidak ada kontak dengan nomor telepon yang ditemukan
@@ -62,19 +58,16 @@
Coba sembunyikan duplikat kontak
Kelola tab yang ditampilkan
Kontak
- Favorit
Tampilkan dialog konfirmasi panggilan sebelum melakukan panggilan
Hanya tampilkan kontak dengan nomor telepon
Tampilkan huruf pada tombol dial
- Surel
Rumah
Kerja
Lainnya
- Nomor
Ponsel
Utama
Faks Kerja
@@ -88,9 +81,6 @@
Sepertinya anda belum menambahkan kontak favorit.
- Tambah favorit
- Tambah ke favorit
- Buang dari favorit
Anda harus berada di layar Sunting untuk mengubah kontak
@@ -113,13 +103,14 @@
Telepon
- Memanggil
- Panggilan masuk
- Panggilan masuk dari…
- Panggilan keluar
- Terputus
- Tolak
- Jawab
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Surel
Alamat
Acara (ulang tahun, hari jadi)
- Catatan
Organisasi
Situs web
Grup
Sumber kontak
Pesan singkat (IM)
-
- Kelola nomor yang diblokir
- Anda tidak memblokir siapapun.
- Tambahkan nomor yang diblokir
- Blokir nomor
- Blokir nomor
- Nomor yang diblokir
-
- Apakah anda yakin ingin menghapus %s?
Kontak akan dihapus dari semua sumber kontak.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Aplikasi untuk mengelola kontak tanpa iklan, menghargai privasi anda.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Aplikasi sederhana untuk membuat dan mengelola kontak anda dari berbagai sumber. Kontak bisa disimpan hanya pada perangkat anda, tetapi juga disinkronisasikan melalui Google, atau akun lainnya. Anda bisa menampilkan kontak favorit anda pada daftar terpisah.
+ 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.
- Anda juga bisa menggunakannya untuk mengelola surel dan acara. Memiliki kemampuan untuk mengurutkan/menyaring menggunakan banyak parameter, dan secara opsional menampilkan nama belakang sebagai awalan nama.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Tanpa iklan dan perizinan yang tidak perlu. Sepenuhnya sumber terbuka, dengan warna yang bisa disesuaikan.
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index ed4f33a9..4622cc2b 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -14,14 +14,10 @@
Invia un SMS al gruppo
Invia un\'email al gruppo
Chiama %s
- Richiedi le permissioni necessarie
Crea un nuovo contatto
Aggiungi a un contatto esistente
- È necessario impostare quest\'app come predefinita per utilizzare i numeri bloccati.
- Imposta come predefinita
- Nessun contatto trovato.
Nessun contatto trovato con un\'email
Nessun contatto trovato con un numero di telefono
@@ -62,19 +58,16 @@
Unisci i contatti duplicati
Gestisci le schede mostrate
Contatti
- Preferiti
Mostra un messaggio di conferma prima di iniziare una chiamata
Mostra solamente i contatti con almeno un numero telefonico
Mostra lettere nel compositore
- Email
Casa
Lavoro
Altro
- Numero
Cellulare
Principale
Fax di lavoro
@@ -88,9 +81,6 @@
Non si ha ancora nessun contatto preferito.
- Aggiungi preferito
- Aggiungi ai preferiti
- Rimuovi dai preferiti
Si deve stare nella schermata di modifica per modificare un contatto
@@ -113,13 +103,14 @@
Compositore
- Chiamata in corso
- Chiamata in arrivo
- Chiamata in arrivo da…
- Chiamata in corso
- Disconnesso
- Rifiuta
- Rispondi
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Email
Indirizzi
Eventi (compleanni, anniversari)
- Note
Organizazione
Siti web
Gruppi
Provenienza del contatto
Messaggistica istantanea (IM)
-
- Gestisci i numeri bloccati
- Nessun numero bloccato.
- Aggiungi un numero da bloccare
- Blocca numero
- Blocca numeri
- Numeri bloccati
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Un\'app per gestire i propri contatti senza pubblicità, che rispetta la privacy.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Una semplice applicazione per creare o gestire i propri contatti di qualsiasi tipo. I contatti saranno salvati nel dispositivo, ma possono essere eventualmente sincronizzati con Google o con altri servizi. Si possono visualizzare i contatti preferiti in una lista separata.
+ 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.
- Si può utilizzare l\'applicazione anche per gestire l\'email e gli eventi. Si possono ordinare e filtrare per diversi criteri, opzionalmente si può visualizzare il cognome come nome.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
L\'applicazione non contiene pubblicità o permessi non necessari; è completamente opensource e la si può personalizzare con i propri colori preferiti.
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 91470334..df600c95 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -14,14 +14,10 @@
グループにSMSを送信
グループにメールを送信
Call %s
- Request the required permissions
新しい連絡先を作成
既存の連絡先に追加
- You have to make this app the default dialer app to make use of blocked numbers.
- Set as default
- 連絡先が見つかりません.
メールアドレスが登録された連絡先が見つかりません
電話番号が登録された連絡先が見つかりません
@@ -62,19 +58,16 @@
重複した連絡先を除外する
表示するタブを管理
連絡先
- お気に入り
発信する前に確認ダイアログを表示する
電話番号が登録された連絡先のみ表示する
Show letters on the dialpad
- メール
自宅
職場
その他
- 番号
携帯
Main
職場FAX
@@ -88,9 +81,6 @@
お気に入りの連絡先はまだありません
- お気に入りを追加
- お気に入りに追加
- お気に入りから削除
連絡先を編集するには編集画面に切り替えてください
@@ -113,13 +103,14 @@
電話
- 発信中
- 着信中
- 着信中…
- 通話中
- 切断されました
- 拒否
- 応答
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
メール
住所
予定 (誕生日、記念日)
- メモ
所属
ウェブサイト
グループ
インポート元
Instant messaging (IM)
-
- ブロックした番号を管理
- まだ誰もブロックしていません.
- ブロックする番号を追加
- Block number
- Block numbers
- Blocked numbers
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- An app for managing your contacts without ads, respecting your privacy.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- 連絡先を作成または管理するためのシンプルなアプリです。連絡先はお使いの端末上にのみ保存されますが、Googleや他のアカウントと同期することもできます。お気に入りの連絡先を別のリストとして表示することができます。
+ 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.
- 友人のメールアドレスや予定の管理にも使用できます。これらは複数の項目で並べ替えやフィルタリングする機能があり、名前を\"姓 名\"の順に表示することもできます。
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
広告や不要なアクセス許可は含まれていません。完全にオープンソースで、色のカスタマイズも可能です。
diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml
index 56feee04..ed878c5f 100644
--- a/app/src/main/res/values-ko-rKR/strings.xml
+++ b/app/src/main/res/values-ko-rKR/strings.xml
@@ -14,14 +14,10 @@
Send SMS to group
Send email to group
Call %s
- Request the required permissions
Create new contact
Add to an existing contact
- You have to make this app the default dialer app to make use of blocked numbers.
- Set as default
- No contacts found.
No contacts with emails have been found
No contacts with phone numbers have been found
@@ -62,19 +58,16 @@
Try filtering out duplicate contacts
Manage shown tabs
Contacts
- Favorites
Show a call confirmation dialog before initiating a call
Show only contacts with phone numbers
Show letters on the dialpad
- 이메일
집
회사
미분류
- 연락처
핸드폰
Main
회사 팩스
@@ -88,9 +81,6 @@
자주 사용하는 연락처가 아직 등록되지 않았습니다.
- 자주쓰는 연락처 등록
- 자주쓰는 연락처에 추가
- 자주쓰는 연락처에서 제거
You must be at the Edit screen to modify a contact
@@ -113,13 +103,14 @@
Dialer
- Calling
- Incoming call
- Incoming call from…
- Ongoing call
- Disconnected
- Decline
- Answer
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Emails
Addresses
Events (birthdays, anniversaries)
- Notes
Organization
Websites
Groups
Contact source
Instant messaging (IM)
-
- Manage blocked numbers
- You are not blocking anyone.
- Add a blocked number
- Block number
- Block numbers
- Blocked numbers
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- An app for managing your contacts without ads, respecting your privacy.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- 연락처를 생성하고 관리하는 간단한 앱입니다. 연락처는 기본적으로 기기에만 저장되며 Google 또는 다른 계정을 통해 동기화 할 수도 있습니다. 즐겨 찾는 연락처는 별도의 목록에 표시 할 수 있습니다.
+ 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.
- 다양한 조건으로 정렬 및 필터링이 가능하며 성과 이름을 표시하는 옵션도 제공합니다. 또한 애플리케이션을 이용해 사용자 이메일 및 이벤트 관리를 할 수도 있습니다.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
광고가 포함되어 있거나, 불필요한 권한을 요청하지 않습니다. 이 앱의 모든 소스는 오픈소스이며, 사용자가 직접 애플리케이션의 컬러를 설정 할 수 있습니다.
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 1b79cdf8..354bfd72 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -14,14 +14,10 @@
Send SMS to group
Send email to group
Call %s
- Request the required permissions
Create new contact
Add to an existing contact
- You have to make this app the default dialer app to make use of blocked numbers.
- Set as default
- No contacts found.
No contacts with emails have been found
No contacts with phone numbers have been found
@@ -62,19 +58,16 @@
Try filtering out duplicate contacts
Manage shown tabs
Contacts
- Favorites
Show a call confirmation dialog before initiating a call
Show only contacts with phone numbers
Show letters on the dialpad
- Elektroninis paštas
Namų
Darbo
Kitas
- Numeris
Mobilus
Pagrindinis
Darbo faksas
@@ -88,9 +81,6 @@
Atrodo jog Jūs dar neįvedėte nė vieno mėgiamiausiojo kontakto.
- Pridėti mėgiamiausiuosius
- Pridėti į mėgiamiausiuosius
- Pašalinti iš mėgiamiausiųjų
You must be at the Edit screen to modify a contact
@@ -113,13 +103,14 @@
Dialer
- Calling
- Incoming call
- Incoming call from…
- Ongoing call
- Disconnected
- Decline
- Answer
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Elektroniniaipaštai
Adresai
Įvykiai (gimtadieniai, sukaktys)
- Užrašai
Organizacija
Interneto puslapiai
Grupės
Kontakto šaltinis
Instant messaging (IM)
-
- Manage blocked numbers
- You are not blocking anyone.
- Add a blocked number
- Block number
- Block numbers
- Blocked numbers
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- An app for managing your contacts without ads, respecting your privacy.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Paprasta programėlė įrenginio kontaktų kūrimui ir tvarkymui iš įvairių šaltinių. Kontaktai gali būti saugomi Jūsų įrenginyje, taip pat sinchronizuojami per Google, ar kitas paskyras. Jūs galite matyti mėgiamiausiuosius kontaktus atskirame sąraše.
+ 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.
- Jūs taip pat galite naudoti programėlę elektroninių paštų adresų tvarkymui. Programėlė turi galimybę rikiuoti/filtruoti pagal įvairius parametrus, taip pat rodyti pavardę pirma vardo.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Neturi reklamų ar nereikalingų leidimų. Programėlė visiškai atviro kodo, yra galimybė keisti spalvas.
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 31a132e4..5c63a8b5 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -14,14 +14,10 @@
SMS naar groep sturen
E-mail naar groep sturen
%s bellen
- Om benodigde machtigingen vragen
Nieuw contact
Aan bestaand contact toevoegen
- Maak van deze app de standaardapp voor bellen om nummers te kunnen blokkeren.
- Als standaard instellen
- Geen contacten gevonden.
Geen contacten met e-mailadressen gevonden
Geen contacten met telefoonnummers gevonden
@@ -62,19 +58,16 @@
Probeer dubbele contacten weg te filteren
Tabs tonen/verbergen
Contacten
- Favorieten
Om bevestiging vragen voor het bellen
Alleen contacten met telefoonnummers tonen
Letters op het toetsenblok tonen
- E-mail
Thuis
Werk
Overig
- Nummer
Mobiel
Standaard
Fax Werk
@@ -88,9 +81,6 @@
Er zijn nog geen favorieten toegevoegd.
- Favorieten toevoegen
- Aan favorieten toevoegen
- Uit favorieten verwijderen
Ga naar Contact bewerken om gegevens aan te passen
@@ -113,13 +103,14 @@
Bellen
- Bellen
- Inkomend gesprek
- Inkomend gesprek van…
- Lopend gesprek
- Verbinding verbroken
- Afwijzen
- Opnemen
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Snelkiezer
@@ -134,23 +125,13 @@
E-mailadressen
Adressen
Datums (verjaardagen, jubileums)
- Notities
Organisatie
Websites
Groepen
Account
Instant messaging (IM)
-
- Geblokkeerde nummers beheren
- Geen geblokkeerde nummers.
- Geblokkeerd nummer toevoegen
- Nummer blokkeren
- Nummers blokkeren
- Geblokkeerde nummers
-
- %s verwijderen?
De contactpersoon zal worden verwijderd uit alle accounts.
@@ -172,18 +153,40 @@
Eenvoudig Adresboek Pro - Snel contacten beheren
- Een privacyvriendelijke advertentievrije app om uw contacten te beheren.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Een eenvoudige app om contacten aan te maken en te beheren. Contacten kunnen lokaal opgeslagen worden, of gesynchroniseerd via Google en andere accounts. Favoriete contacten kunnen in een aparte lijst worden getoond.
+ 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.
- De app is ook te gebruiken voor het beheren van e-mailadressen en gebeurtenissen gekoppeld aan contacten. Sorteren en filteren is mogelijk op basis van verschillende parameters en zowel voor- als achternaam kan als eerste worden getoond.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Bevat geen advertenties of onnodige machtigingen. Volledig open-source. Kleuren van de app kunnen worden aangepast.
- Check out the full suite of Simple Tools here:
+ Probeer ook eens de andere apps van Simple Tools:
https://www.simplemobiletools.com
- Standalone website of Simple Contacts Pro:
+ Website voor Eenvoudig Adresboek Pro:
https://www.simplemobiletools.com/contacts
Facebook:
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 32260508..42e9932f 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -14,14 +14,10 @@
Wyślij SMS-a do grupy
Wyślij e-maila do grupy
Zadzwoń do: %s
- Wymagaj koniecznych uprawnień
Utwórz nowy kontakt
Dodaj do istniejącego kontaktu
- Musisz ustawić tę aplikację jako domyślną aplikację telefoniczną, aby móc korzystać z funkcji blokowania numerów.
- Ustaw jako domyślną
- Nie znaleziono kontaktów.
Nie znaleziono kontaktów z adresami e-mail
Nie znaleziono kontaktów z numerami telefonów
@@ -62,19 +58,16 @@
Spróbuj odfiltrować zduplikowane kontakty
Zarządzaj pokazywanymi sekcjami
Kontakty
- Ulubione
Pokazuj okno potwierdzenia zadzwonienia przed zainicjonowaniem połączenia
Pokazuj wyłącznie kontakty z numerami telefonów
Pokazuj litery na panelu wybierania
- E-mail
Dom
Praca
Inny
- Numer
Komórkowy
Główny
Służbowy faks
@@ -88,9 +81,6 @@
Wygląda na to, że nie dodałeś jeszcze żadnego ulubionego kontaktu.
- Dodaj ulubione
- Dodaj do ulubionych
- Usuń z ulubionych
Musisz wejść do ekranu edycji, aby zmodyfikować kontakt
@@ -113,13 +103,14 @@
Dialer
- Dzwonienie
- Połączenie przychodzące
- Połączenie przychodzące od…
- Połączenie wychodzące
- Rozłączony
- Odrzuć
- Odpowiedz
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
E-maile
Adresy
Wydarzenia (urodziny, rocznice)
- Notatki
Organizacja
Strony internetowe
Grupy
Miejsce przechowywania kontaktu
Komunikator
-
- Zarządzaj zablokowanymi numerami
- Nie blokujesz nikogo.
- Dodaj numer do blokowania
- Zablokuj numer
- Zablokuj numery
- Zablokowane numery
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Aplikacja do zarządzania Twoimi kontaktami, bez reklam, szanująca prywatność.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Prosta aplikacja do tworzenia lub zarządzania Twoimi kontaktami przechowywanymi w różnych miejscach. Kontakty mogą być przechowywane tylko na Twoim urządzeniu, ale również synchronizowane przez konto Google lub inne konta. Możesz wyświetlać Twoje ulubione kontakty na oddzielnej liście.
-
- Możesz użyć jej także do zarządzania e-mailami użytkowników i wydarzeniami. Jest zdolna do sortowania/filtrowania według wielu parametrów, opcjonalnie do wyświetlania nazwiska jako imienia.
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Nie zawiera reklam oraz niekoniecznych uprawnień. Jest w pełni otwartoźródłowa i w pełni podatna na kolorowanie.
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 1211e84f..8cabf290 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -14,14 +14,10 @@
Enviar SMS ao grupo
Enviar e-mail ao grupo
Ligar para %s
- Pedir as permissões necessárias
Criar novo contato
Adicionar um contato existente
- Você precisa tornar este aplicativo padrão para poder bloquear números.
- Definir como padrão
- Nenhum contato encontrado.
Não foram encontrados contatos com e-mails
Não foram encontrados contatos com números de telefone
@@ -62,19 +58,16 @@
Tentar filtrar contatos duplicados
Gerenciar abas visíveis
Contatos
- Favoritos
Mostrar diálogo para confirmar a chamada antes de ligar
Mostar apenas os contatos com número de telefone
Mostrar letras no discador
- E-mail
Residencial
Comercial
Outro
- Número
Celular
Principal
Fax Comercial
@@ -88,9 +81,6 @@
Parece que você ainda não adicionou nenhum contato favorito.
- Adicionar favoritos
- Adicionar aos favoritos
- Remover dos favoritos
Você deve estar na tela de edição para modificar um contato
@@ -113,13 +103,14 @@
Discador
- Chamando
- Chamada recebida
- Chamada recebida de…
- Chamada efetuada
- Desligada
- Recusar
- Atender
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
E-mails
Endereços
Eventos (aniversários, datas especiais)
- Notas
Empresa
Páginas Web
Grupos
Origem do contato
Mensageiro instantâneo (MI)
-
- Gerenciar números bloqueados
- Não há números bloqueados.
- Adicionar um número a bloquear
- Bloquear número
- Bloquear números
- Números bloqueados
-
- Você tem certeza que quer deletar %s?
O contato será removido de todas removido de todas as fontes de contato.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Um aplicativo para gerenciar os seus contatos que respeita a sua privacidade.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Um aplicativo simples para criar ou gerenciar seus contatos a partir de qualquer origem. Os contatos podem ser armazenados apenas no seu dispositivo, mas também podem ser sincronizados através do Google ou outras contas. Seus contatos favoritos podem ser apresentados em uma lista separada.
+ 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.
- Você também pode usá-lo para gerenciar os eventos e e-mails do usuário. Tem a capacidade de ordenar/filtrar através de diversos parâmetros e, opcionalmente, apresentar o sobrenome antes do primeiro nome.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Não contém anúncios e permissões desnecessárias. É totalmente open source e permite a personalização das cores.
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 389afd45..d0921e3f 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -14,14 +14,10 @@
Enviar SMS para o grupo
Enviar e-mail para o grupo
Ligar a %s
- Pedir permissão necessária
Criar novo contacto
Adicionar a contacto existente
- Tem que tornar esta a aplicação padrão para poder bloquear números.
- Definir como padrão
- Não existem contactos.
Não existem contactos com endereço de e-mail
Não existem contactos com número de telefone
@@ -62,19 +58,16 @@
Tentar filtrar contactos duplicados
Gerir separadores a exibir
Contactos
- Favoritos
Mostrar diálogo para confirmar a chamada
Mostrar apenas contactos com número de telefone
Mostrar letras no marcador
- E-mail
Pessoal
Profissional
Outro
- Número
Telemóvel
Principal
Fax profissional
@@ -88,9 +81,6 @@
Parece que ainda não adicionou contactos aos favoritos
- Adicionar favoritos
- Adicionar aos favoritos
- Remover dos favoritos
Tem que estar no ecrã de edição para alterar um contacto
@@ -113,13 +103,14 @@
Marcador
- A chamar
- Chamada recebida
- Chamada recebida de…
- Chamada efetuada
- Desligada
- Recusar
- Atender
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Ligação rápida
@@ -134,23 +125,13 @@
E-mail
Endereço
Eventos (data de nascimento, aniversário)
- Notas
Organização
Site
Grupos
Origem do contacto
Mensagem instantânea (IM)
-
- Gerir números bloqueados
- Não existem números bloqueados
- Adicionar um número a bloquear
- Bloquear número
- Bloquear números
- Números bloqueados
-
- Tem a certeza de que deseja apagar %s?
O contacto será apagado de todas as origens.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Gestão de contactos
- Aplicação para gerir os seus contactos, sem anúncios e com total privacidade.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Uma aplicação básica para criar e gerir contactos. Pode utilizar a aplicação guardar os contactos localmente, na sua conta Google ou em outro tipo de contas. Pode mostrar os contactos favoritos numa lista distinta.
+ 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.
- Também pode utilizar a aplicação para gerir endereços de e-mail e eventos. Tem a capacidade de ordenar/filtrar por diversos parâmetros e também a possibilidade de diversas formas de exibição dos contactos.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Não contém anúncios ou permissões desnecessárias. É totalmente open source e permite personalização de cores.
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index d30fe2de..c11ff8d5 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -14,14 +14,10 @@
Отправить SMS группе
Отправить письмо группе
Вызов %s
- Запрос необходимых разрешений
Создать новый контакт
Добавить к существующему контакту
- Вы должны сделать \"Simple Contacts\" приложением по умолчанию для набора номера, чтобы использовать блокировку номеров.
- Установить по умолчанию
- Контакты не найдены.
Контакты с адресами электронной почты не найдены
Контакты с номерами телефонов не найдены
@@ -62,19 +58,16 @@
Отфильтровывать повторяющиеся контакты
Управление отображаемыми вкладками
Контакты
- Избранное
Показывать диалог подтверждения вызова
Показывать только контакты с номерами телефонов
Показывать буквы на кнопках набора номера
- Эл. почта
Домашний
Рабочий
Другой
- Номер
Мобильный
Основной
Рабочий факс
@@ -88,9 +81,6 @@
Похоже, вы ещё не добавили избранные контакты.
- Добавить избранные
- Добавить в избранные
- Удалить из избранных
Для изменения контакта необходимо находиться на экране редактирования
@@ -113,13 +103,14 @@
Номеронабиратель
- Вызов
- Входящий вызов
- Входящий вызов от…
- Исходящий вызов
- Разъединено
- Отклонить
- Ответить
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Быстрый набор
@@ -134,23 +125,13 @@
Электронная почта
Адреса
События (дни рождения, юбилеи)
- Заметки
Организация
Сайты
Группы
Источник контакта
Мессенджеры
-
- Управление блокируемыми номерами
- Нет блокируемых номеров
- Добавить блокируемый номер
- Блокируемый номер
- Блокируемые номера
- Заблокированные номера
-
- Вы уверены, что хотите удалить %s?
Контакт будет удалён из всех источников контактов.
@@ -174,11 +155,33 @@
Simple Contacts Pro - легко управляйте контактами
- Приложение для управления контактами. Конфиденциальное. Без рекламы.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Простое приложение для создания и управления контактами из любого источника. Контакты могут быть сохранены только на вашем устройстве, а также синхронизированы через Google или другие учётные записи. Вы можете отображать свои любимые контакты в отдельном списке.
+ 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.
- Вы можете использовать его для управления электронной почтой и событиями контактов. Приложение имеет возможность сортировать/фильтровать по нескольким параметрам, по желанию отображать фамилию в качестве имени.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Не содержит рекламы или ненужных разрешений, полностью с открытым исходным кодом. Есть возможность настраивать цвета.
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index a50b8a0d..53ed85a2 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -14,14 +14,10 @@
Poslať skupine SMS
Poslať skupine email
Zavolať %s
- Vyžiadať potrebné oprávnenia
Vytvoriť nový kontakt
Pridať k existujúcemu kontaktu
- Pre použitie blokovania čísel musíte nastaviť aplikáciu ako predvolenú pre správu hovorov.
- Nastaviť ako predvolenú
- Nenašli sa žiadne kontakty.
Nenašli sa žiadne kontakty s emailami
Nenašli sa žiadne kontakty s telefónnymi číslami
@@ -62,19 +58,16 @@
Pokúsiť sa vyfiltrovať duplicitné kontakty
Spravovať zobrazené karty
Kontakty
- Obľúbené
Zobraziť pred spustením hovoru okno na jeho potvrdenie
Zobraziť iba kontakty s telefónnymi číslami
Zobraziť na číselníku písmená
- Email
Domov
Práca
Iné
- Číslo
Mobil
Hlavné
Pracovný fax
@@ -88,9 +81,6 @@
Zdá sa, že ste ešte nepridali žiadne obľúbené kontakty.
- Pridať obľúbené
- Pridať medzi obľúbené
- Odstrániť z obľúbených
Pre úpravu kontaktu musíte byť v Editore kontaktu
@@ -113,13 +103,14 @@
Telefón
- Vytáča sa
- Prichádzajúci hovor
- Prichádzajúci hovor od…
+ Prijať
+ Odmietnuť
+ Neznámy volajúci
+ Vám volá
+ Vytáčanie
+ Hovor ukončený
+ Hovor končí
Prebiehajúci hovor
- Ukončený hovor
- Odmietnuť
- Prijať
Rýchle vytáčanie
@@ -134,23 +125,13 @@
Emaily
Adresy
Udalosti (narodeniny, výročia)
- Poznámky
Firma
Webstránky
Skupiny
Zdroje kontaktov
Rýchle správy (IM)
-
- Spravovať blokované čísla
- Neblokujete nikoho.
- Pridať blokované číslo
- Blokovať číslo
- Blokovať čísla
- Blokované čísla
-
- Ste si istý, že chcete vymazať %s?
Kontakt bude vymazaný zo všetkých zdrojov kontaktov.
@@ -174,11 +155,33 @@
Jednoduché kontakty Pro - Rýchla správa kontaktov
- Apka na správu vašich kontaktov bez reklám, rešpektujúca vaše súkromie.
+ Rýchly spôsob na správu kontaktov bez reklám, podporuje aj skupiny a obľúbené.
- Jednoduchá aplikácia na vytváranie, alebo správu vašich kontaktov z rôznych zdrojov. Môžu byť uložené buď iba vo vašom zariadení, alebo ich môžete aj synchronizovať cez Google, alebo iný účet. Vaše obľúbené položky viete zobraziť vo vlastnom zozname.
+ 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.
- Viete ju používať aj na správu emailov a udalostí kontaktov. Ponúka možnosť zoradenia/filtrovania pomocou rôznych parametrov, voliteľné je zobrazovanie priezviska ako prvého mena.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Neobsahuje žiadne reklamy a nepotrebné oprávnenia. Je opensource, poskytuje možnosť zmeny farieb.
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 99c791d1..ef29bb81 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -14,14 +14,10 @@
Skicka sms till grupp
Skicka e-post till grupp
Ring %s
- Begär de behörigheter som krävs
Skapa ny kontakt
Lägg till i en befintlig kontakt
- Du måste ställa in den här appen som standardtelefonapp för att kunna använda blockerade nummer.
- Ange som standard
- Inga kontakter hittades.
Inga kontakter med e-postadresser hittades
Inga kontakter med telefonnummer hittades
@@ -62,19 +58,16 @@
Försök filtrera bort dubblettkontakter
Välj vilka flikar som ska visas
Kontakter
- Favoriter
Visa en bekräftelsedialogruta före uppringning
Visa bara kontakter med telefonnummer
Visa bokstäver på knappsatsen
- E-post
Hem
Arbete
Annat
- Nummer
Mobil
Primärt nummer
Arbetsfax
@@ -88,9 +81,6 @@
Det verkar som att du inte har lagt till några favoritkontakter ännu.
- Lägg till favoriter
- Lägg till i favoriter
- Ta bort från favoriter
Kontakter kan bara redigeras i redigeringsvyn
@@ -113,13 +103,14 @@
Telefon
- Ringer
- Inkommande samtal
- Inkommande samtal från…
- Pågående samtal
- Frånkopplad
- Avvisa
- Svara
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
E-postadresser
Adresser
Händelser (födelsedagar, årsdagar)
- Anteckningar
Organisation
Webbplatser
Grupper
Kontaktkälla
Snabbmeddelanden (IM)
-
- Hantera blockerade nummer
- Du blockerar inte någon.
- Lägg till ett blockerat nummer
- Blockera nummer
- Blockera nummer
- Blockerade nummer
-
- Är du säker på att du vill ta bort %s?
Kontakten kommer att tas bort från alla kontaktkällor.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- En app för att hantera dina kontakter utan reklam, respekterar din integritet.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- En enkel app för att skapa och hantera kontakter från olika källor. Kontakterna kan lagras bara på din enhet, men kan också synkroniseras med ditt Google-konto eller andra konton. Du kan visa dina favoritkontakter i en separat lista.
+ 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.
- Du kan också använda den för att hantera dina kontakters e-postadresser och händelser. Den kan sortera och filtrera efter flera parametrar. Den kan också visa efternamn först.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Innehåller ingen reklam eller onödiga behörigheter. Den har helt öppen källkod och anpassningsbara färger.
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index a4dbb699..fea41639 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -14,14 +14,10 @@
Gruba SMS gönder
Gruba e-posta gönder
%s kişisini ara
- Gerekli izinleri iste
Yeni kişi oluştur
Mevcut bir kişiye ekle
- Engellenen numaraları kullanmak için bu uygulamayı varsayılan çevirici uygulaması yapmalısınız.
- Varsayılan olarak ayarla
- Kişi bulunamadı.
E-posta ile hiç bağlantı bulunamadı
Telefon numaralarını içeren kişi bulunamadı
@@ -62,19 +58,16 @@
Çift kişileri filtrelemeyi dene
Gösterilen sekmeleri yönet
Kişiler
- Favoriler
Arama başlatmadan önce arama onayı penceresi göster
Sadece telefon numaralarını içeren kişileri göster
Tuş takımında harfleri göster
- E-posta
Ev
İş
Diğer
- Numara
Cep
Ana
İş Faksı
@@ -88,9 +81,6 @@
Henüz herhangi bir favori kişi eklememişsiniz.
- Favorileri ekle
- Favorilere ekle
- Favorilerden kaldır
Bir kişiyi değiştirmek için Düzen ekranında olmalısınız
@@ -113,13 +103,14 @@
Çevirici
- Çağrı yapılıyor
- Gelen çağrı
- Gelen çağrı şundan…
- Devam eden çağrı
- Bağlantı kesildi
- Reddet
- Cevapla
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Hızlı arama
@@ -134,23 +125,13 @@
E-postalar
Adresler
Etkinlikler (doğum günleri, yıldönümleri)
- Notlar
Organizasyon
Web siteleri
Gruplar
Kişi kaynağı
Anlık mesajlaşma (IM)
-
- Engelli numaraları yönet
- Kimseyi engellemiyorsun.
- Engellenen numara ekle
- Numarayı engelle
- Numaraları engelle
- Engelli numaralar
-
- %s silmek istediğinize emin misiniz?
Kişi tüm kişi kaynaklarından kaldırılacak.
@@ -172,18 +153,40 @@
Basit Kişiler Pro - Kişilerinizi kolayca yönetin
- Reklamsız, gizliliğinize saygı duyan, kişilerinizi yönetmek için bir uygulama.
+ Reklamsız, grup ve sık kullanılanlarla kolay ve hızlı kişi yönetimi.
- Kişilerinizi herhangi bir kaynaktan oluşturmak veya yönetmek için basit bir uygulama. Kişiler yalnızca cihazınızda saklanabilir, aynı zamanda Google veya diğer hesaplarla senkronize edilebilir. Favori kişilerinizi ayrı bir listede görüntüleyebilirsiniz.
+ Milyonlarca insan tarafından sevilen kişilerinizi yönetmek için hafif bir uygulama. Kişiler yalnızca cihazınızda depolanabilir, aynı zamanda Google veya diğer hesaplarla da senkronize edilebilir.
- Kullanıcı e-postalarını ve etkinliklerini yönetmek için de kullanabilirsiniz. Birden çok parametreye göre sıralama/filtreleme, isteğe bağlı olarak soyadı ilk ad olarak görüntüleme yeteneğine sahiptir.
+ Kullanıcı e-postalarını ve etkinliklerini yönetmek için de kullanabilirsiniz. Birden fazla parametreye göre sıralama/filtreleme, isteğe bağlı olarak soyadı ilk ad olarak görüntüleme yeteneğine sahiptir.
+
+ Favori kişilerinizi veya gruplarınızı ayrı bir listede görüntüleyebilirsiniz. Gruplar toplu e-postalar veya SMS göndermek için kullanılabilir, size biraz zaman kazandırmak için bunları kolayca yeniden adlandırabilirsiniz.
+
+ Kişilerinizi aramak veya mesaj göndermek için kullanışlı düğmeler içerir. Tüm görünür alanlar istediğiniz gibi özelleştirilebilir, kullanılmayanları kolayca gizleyebilirsiniz. Arama işlevi, istediğiniz kişiyi kolayca bulabilmeniz için, verilen dizeyi görünür her kişi alanında arayacaktır.
+
+ Akıllı kişi önerileri ile hizmetinizde hafif bir tuş takımı da var.
+
+ Kolay taşıma veya verilerinizi yedeklemek için vCard biçimindeki kişileri .vcf dosyalarına dışa aktarmayı/içe aktarmayı destekler.
+
+ Bu modern ve kararlı kişi yöneticisi ile kişilerinizi diğer uygulamalarla paylaşmayarak koruyabilir, böylece kişilerinizi gizli tutabilirsiniz.
+
+ Kişi kaynağı gibi, kişi adını, e-postayı, telefon numarasını, adresi, organizasyonu, grupları ve diğer birçok özelleştirilebilir alanı da kolayca değiştirebilirsiniz. Doğum günleri, yıldönümleri veya diğer özel etkinlikler gibi kişi etkinliklerini depolamak için de kullanabilirsiniz.
+
+ Bu basit kişi düzenleyicisi, ana ekranda telefon numaralarını göstermek, kişi küçük resminin görünürlüğünü değiştirmek, yalnızca telefon numaralarına sahip kişileri göstermek, bir arama başlatmadan önce bir arama onay kutusu göstermek gibi birçok kullanışlı ayara sahiptir. Harfleri de kullanan hızlı bir çevirici ile birlikte gelir.
+
+ Kullanıcı deneyimini daha da geliştirmek için, bir kişiye tıklandığında neler olacağını özelleştirebilirsiniz. Bir çağrı başlatabilir, Ayrıntıları Görüntüle ekranına gidebilir veya seçilen kişiyi düzenleyebilirsiniz.
+
+ İstenmeyen gelen aramaları önlemek için telefon numaralarını kolayca engelleyebilirsiniz.
+
+ Potansiyel olarak istenmeyen kişileri göstermekten kaçınmak için güçlü bir dahili yinelenen kişi birleştirici vardır.
+
+ Varsayılan olarak materyal tasarımı ve koyu tema ile birlikte gelir, kolay kullanım için mükemmel kullanıcı deneyimi sağlar. İnternet erişiminin olmaması, diğer uygulamalardan daha fazla gizlilik, güvenlik ve kararlılık sağlar.
Reklam veya gereksiz izinler içermez. Tamamen açık kaynaktır, özelleştirilebilir renkler sağlar.
Basit Araçlar paketinin tamamını buradan inceleyin:
https://www.simplemobiletools.com
- Basit Kişiler Pro'nun bağımsız web sitesi:
+ Basit Kişiler Pro\'nun bağımsız web sitesi:
https://www.simplemobiletools.com/contacts
Facebook:
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 91856324..a2fc5223 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -14,14 +14,10 @@
Надіслати SMS групі контактів
Надіслати електронний лист групі контактів
Телефонувати %s
- Запит на необхідні дозволи
Створити новий контакт
Додати до існуючого контакту
- Щоб використовувати функцію блокування номерів, вам необхідно встановити цей додаток як стандартний для роботи з контактами.
- Встановити додаток як стандартний
- Контактів не знайдено.
Не знайдено контактів з електронною поштою
Не знайдено контактів з телефонними номерами
@@ -62,19 +58,16 @@
Фільтрувати контакти, що дублюються
Керування вкладками, що відображаються
Контакти
- Улюблені
Показувати діалог підтвердження виклику
Показувати лише контакти з телефонними номерами
Показувати літери на панелі набору
- Електронна пошта
Домашній
Робочий
Інше
- Номер
Мобільний
Основний
Робочий факс
@@ -88,9 +81,6 @@
Здається, ви ще не додали улюблені контакти.
- Додати улюблені
- Додати до улюблених
- Видалити з улюблених
Для того, щоб змінити контакт, необхідно відкрити меню редагування
@@ -113,13 +103,14 @@
Набір номера
- Телефоную
- Вхідний виклик
- Вхідний виклик від…
- Триває виклик
- Роз\'єднано
- Відхилити
- Відповісти
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Електронна пошта
Адреси
Події (дні народжень, річниці)
- Нотатки
Організація
Веб-сайт
Групи
Походження контакту
Мессенджери
-
- Керувати блокованими номерами
- Немає блокованих номерів.
- Додати номер до блокованих
- Блокувати номер
- Блокувати номери
- Заблоковані номери
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- Додаток для керування вашими контактами, без реклами, з увагою на приватності.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- Простий додаток для створення або керування вашими контактами з будь-якого джерела. Контакти можна зберігати лише на вашому пристроєві, а також синхронізувати з допомогою Google чи інших служб. Ви можете розмістити ваші улюблені контакти в окремому списку.
+ 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.
- Ви також можете використовувати його для керування електронними поштовими адресами та подіями. У нього є можливість сортувати/фільтрувати за численними параметрами, за бажанням відображати прізвище як ім\'я.
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
Цей додаток не буде показувати рекламу, потрібні лише найнеобхідніші дозволи. Додаток має повністю відкритий програмний код, кольори оформлення можна легко налаштувати.
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index b24b5799..eb60dfe3 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -14,14 +14,10 @@
发送短信给群组
发送电子邮件给群组
打电话给 %s
- 请求必要的权限
建立新联系人
添加至已存在的联系人
- 你必须将这应用程序设为默认的拨号程序来使用黑名单。
- 设为默认
- 未发现联系人.
未发现含有电子邮箱的联系人
未发现含有电话号码的联系人
@@ -62,19 +58,16 @@
试着过滤重复的联系人
管理显示的页面
联系人
- 我的收藏
开始通话前显示通话确认框
只显示含有电话话码的联系人
在拨号界面上显示字母
- 电子邮箱
住家
工作
其它
- 号码
手机
主用
工作传真
@@ -88,9 +81,6 @@
你似乎还没加入任何我的收藏联系人。
- 添加我的收藏
- 加入我的收藏
- 从我的收藏移除
你必须在编辑页面去修改联系人
@@ -113,13 +103,14 @@
拨号器
- 拨号中
- 来电
- 通话来自于…
- 持续通话
- 未接电话
- 挂断电话
- 回拨
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
电子邮箱
地址
活动 (生日、纪念日)
- 笔记
组织
网站
群组
联系人来源
即时通讯 (IM)
-
- 管理黑名单
- 你的黑名单为空
- 添加黑名单号码
- 加入黑名单
- 加入黑名单
- 黑名单
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,11 +153,33 @@
Simple Contacts Pro - Manage your contacts easily
- 一个没有广告的通讯录应用程序,用来管理联系人,并且尊重您的隐私。
+ Easy and quick contact management with no ads, handles groups and favorites too.
- 一个用来从任何来源建立或管理联系人的简约应用程序。联系人只能储存于你的设备上,不过也能透过Google或其他帐号来同步。你能将我的收藏联系人显示在独立名单上。
+ 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.
- 你也能用来管理使用者信箱和活动。它能够以多项参数来排序/筛选,以及选择将姓氏显示在名字前面。
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
不包含广告及非必要的权限,而且完全开放源代码,并提供自定义颜色。
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 6b560622..5e834514 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -14,14 +14,10 @@
發送簡訊給群組
發送電子郵件給群組
打電話給 %s
- 請求必要的權限
建立新聯絡人
添加至已存在的聯絡人
- 你必須將這應用程式設為預設的撥號程式來使用黑名單。
- 設為預設
- 未發現聯絡人。
未發現含有電子信箱的聯絡人
未發現含有電話號碼的聯絡人
@@ -62,19 +58,16 @@
試著過濾重複的聯絡人
管理顯示的頁面
聯絡人
- 我的最愛
開始通話前顯示通話確認框
只顯示含有電話話碼的聯絡人
在撥號畫面上顯示字母
- 電子信箱
住家
工作
其它
- 號碼
手機
主用
工作傳真
@@ -88,9 +81,6 @@
你似乎還沒加入任何我的最愛聯絡人。
- 添加我的最愛
- 加入我的最愛
- 從我的最愛移除
你必須在編輯畫面去修改聯絡人
@@ -113,13 +103,14 @@
撥號器
- 撥號中
- 來電
- 通話來自於…
- 持續通話
- 未接電話
- 掛斷電話
- 回撥
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
快速撥號
@@ -134,23 +125,13 @@
電子信箱
地址
活動 (生日、紀念日)
- 筆記
組織
網站
群組
聯絡人來源
即時通訊 (IM)
-
- 管理黑名單
- 你沒有封鎖任何人
- 添加封鎖的號碼
- 封鎖號碼
- 封鎖號碼
- 黑名單
-
- 你確認要刪除 %s 嗎?
這聯絡人將從全部通訊錄來源移除。
@@ -172,11 +153,33 @@
簡易通訊錄 Pro - 輕鬆管理你的聯絡人
- 一個沒有廣告的通訊錄應用程式,用來管理聯絡人,並且尊重您的隱私。
+ Easy and quick contact management with no ads, handles groups and favorites too.
- 一個用來從任何來源建立或管理聯絡人的簡易應用程式。聯絡人只能儲存於你的裝置上,不過也能透過Google或其他帳號來同步。你能將我的最愛聯絡人顯示在獨立名單上。
+ 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.
- 你也能用來管理使用者信箱和活動。它能夠以多項參數來排序/篩選,以及選擇將姓氏顯示在名字前面。
+ 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.
+
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
不包含廣告及非必要的權限,而且完全開放原始碼,並提供自訂顏色。
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..03cc889a
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #33FFFFFF
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 208f75d1..e817ce66 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -3,7 +3,12 @@
88dp
48dp
40dp
+ 60dp
60dp
+ 72dp
+ 30dp
+ 34sp
+ 20sp
34sp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 12fe0982..ddd2a7c2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -14,14 +14,10 @@
Send SMS to group
Send email to group
Call %s
- Request the required permissions
Create new contact
Add to an existing contact
- You have to make this app the default dialer app to make use of blocked numbers.
- Set as default
- No contacts found.
No contacts with emails have been found
No contacts with phone numbers have been found
@@ -62,19 +58,16 @@
Try filtering out duplicate contacts
Manage shown tabs
Contacts
- Favorites
Show a call confirmation dialog before initiating a call
Show only contacts with phone numbers
Show letters on the dialpad
- Email
Home
Work
Other
- Number
Mobile
Main
Work Fax
@@ -88,9 +81,6 @@
Seems like you haven\'t added any favorite contacts yet.
- Add favorites
- Add to favorites
- Remove from favorites
You must be at the Edit screen to modify a contact
@@ -113,13 +103,14 @@
Dialer
- Calling
- Incoming call
- Incoming call from…
- Ongoing call
- Disconnected
- Decline
- Answer
+ Accept
+ Decline
+ Unknown Caller
+ Is Calling
+ Dialing
+ Call Ended
+ Call Ending
+ Ongoing Call
Speed dial
@@ -134,23 +125,13 @@
Emails
Addresses
Events (birthdays, anniversaries)
- Notes
Organization
Websites
Groups
Contact source
Instant messaging (IM)
-
- Manage blocked numbers
- You are not blocking anyone.
- Add a blocked number
- Block number
- Block numbers
- Blocked numbers
-
- Are you sure you want to delete %s?
The contact will be removed from all contact sources.
@@ -172,12 +153,34 @@
Simple Contacts Pro - Manage your contacts easily
- An app for managing your contacts without ads, respecting your privacy.
+ Easy and quick contact management with no ads, handles groups and favorites too.
- A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchronized via Google, or other accounts. You can display your favorite contacts on a separate list.
+ 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.
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.
+ 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.
+
+ 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.
+
+ There is a lightweight dialpad at your service too, with smart contact suggestions.
+
+ It supports exporting/importing contacts in vCard format to .vcf files, for easy migrations or backing up your data.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ You can easily block phone numbers to avoid unwanted incoming calls.
+
+ 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.
+
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
Check out the full suite of Simple Tools here:
diff --git a/build.gradle b/build.gradle
index 2a49b9cb..d991c1f9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.61'
+ ext.kotlin_version = '1.3.72'
repositories {
google()
@@ -9,7 +9,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.3'
+ classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
diff --git a/fastlane/metadata/android/en-US/changelogs/58.txt b/fastlane/metadata/android/en-US/changelogs/58.txt
new file mode 100644
index 00000000..483bd326
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/58.txt
@@ -0,0 +1,2 @@
+ * Fixed some letter scrollbar related glitches
+ * Added some translation and stability related improvements
diff --git a/fastlane/metadata/android/en-US/changelogs/59.txt b/fastlane/metadata/android/en-US/changelogs/59.txt
new file mode 100644
index 00000000..3199d7e8
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/59.txt
@@ -0,0 +1,2 @@
+ * Remember the last used folder at contacts exporting
+ * Do not request the Storage permission on Android 10+, use Scoped Storage
diff --git a/fastlane/metadata/android/en-US/changelogs/60.txt b/fastlane/metadata/android/en-US/changelogs/60.txt
new file mode 100644
index 00000000..d87b6654
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/60.txt
@@ -0,0 +1,4 @@
+ * Use colored contact avatars with the first letter of their name to make the app happier :)
+ * Properly recognize the local Phone contact source, even when empty
+ * Fix some glitches related to the letters in the fastscroller not being correctly sorted
+ * Couple other stability, translation and UI improvements
diff --git a/fastlane/metadata/android/en-US/images/app_icon.png b/fastlane/metadata/android/en-US/images/app_icon.png
new file mode 100644
index 00000000..ea3b512c
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/app_icon.png differ
diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png
index 4394334e..b12afe6a 100644
Binary files a/fastlane/metadata/android/en-US/images/featureGraphic.png and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ
diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png
index b5f3b005..bcd6187d 100644
Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg
index a58fec8d..eddad087 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg
index 21a1c4a7..e9602d8b 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg
index f5bfb8c9..6fcde946 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg
index 20f0db58..a3a5fdc6 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_5.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_5.jpg
index fa387b26..109fbcea 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_5.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_5.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_6.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_6.jpg
deleted file mode 100644
index 9461d736..00000000
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_6.jpg and /dev/null differ
diff --git a/fastlane/metadata/android/en-US/images/promo_graphic.png b/fastlane/metadata/android/en-US/images/promo_graphic.png
new file mode 100644
index 00000000..2d90867c
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/promo_graphic.png differ
diff --git a/fastlane/metadata/android/en-US/images/raw_screenshots/contacts.xcf b/fastlane/metadata/android/en-US/images/raw_screenshots/contacts.xcf
index 59231202..8967b677 100644
Binary files a/fastlane/metadata/android/en-US/images/raw_screenshots/contacts.xcf and b/fastlane/metadata/android/en-US/images/raw_screenshots/contacts.xcf differ
diff --git a/fastlane/metadata/android/en-US/images/square.png b/fastlane/metadata/android/en-US/images/square.png
index 835dd8e6..73e06f18 100644
Binary files a/fastlane/metadata/android/en-US/images/square.png and b/fastlane/metadata/android/en-US/images/square.png differ
diff --git a/fastlane/metadata/android/en-US/images/square.xcf b/fastlane/metadata/android/en-US/images/square.xcf
index 0820e46f..2b28c6d6 100644
Binary files a/fastlane/metadata/android/en-US/images/square.xcf and b/fastlane/metadata/android/en-US/images/square.xcf differ
diff --git a/fastlane/metadata/android/en-US/images/tv_banner.png b/fastlane/metadata/android/en-US/images/tv_banner.png
new file mode 100644
index 00000000..23b8a321
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/tv_banner.png differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d43658d7..8a03306d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Aug 25 14:49:07 CEST 2019
+#Sun Mar 15 21:46:10 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip