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 -Logo +Logo 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