mirror of
				https://github.com/SimpleMobileTools/Simple-Contacts.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	add a new source for storing contacts locally, without sharing with other apps
This commit is contained in:
		| @@ -287,7 +287,7 @@ class EditContactActivity : ContactActivity() { | |||||||
|         contact_source.text = getPublicContactSource(contact!!.source) |         contact_source.text = getPublicContactSource(contact!!.source) | ||||||
|         contact_source.setOnClickListener { |         contact_source.setOnClickListener { | ||||||
|             showContactSourcePicker(contact!!.source) { |             showContactSourcePicker(contact!!.source) { | ||||||
|                 contact!!.source = it |                 contact!!.source = if (it == getString(R.string.phone_storage_hidden)) SMT_PRIVATE else it | ||||||
|                 contact_source.text = getPublicContactSource(it) |                 contact_source.text = getPublicContactSource(it) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ package com.simplemobiletools.contacts.extensions | |||||||
|  |  | ||||||
| import android.content.Intent | import android.content.Intent | ||||||
| import android.net.Uri | import android.net.Uri | ||||||
| import com.simplemobiletools.commons.R |  | ||||||
| import com.simplemobiletools.commons.activities.BaseSimpleActivity | import com.simplemobiletools.commons.activities.BaseSimpleActivity | ||||||
| import com.simplemobiletools.commons.dialogs.RadioGroupDialog | import com.simplemobiletools.commons.dialogs.RadioGroupDialog | ||||||
| import com.simplemobiletools.commons.extensions.getFilePublicUri | import com.simplemobiletools.commons.extensions.getFilePublicUri | ||||||
| @@ -11,6 +10,7 @@ import com.simplemobiletools.commons.extensions.toast | |||||||
| import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE | import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE | ||||||
| import com.simplemobiletools.commons.models.RadioItem | import com.simplemobiletools.commons.models.RadioItem | ||||||
| import com.simplemobiletools.contacts.BuildConfig | import com.simplemobiletools.contacts.BuildConfig | ||||||
|  | import com.simplemobiletools.contacts.R | ||||||
| import com.simplemobiletools.contacts.activities.SimpleActivity | import com.simplemobiletools.contacts.activities.SimpleActivity | ||||||
| import com.simplemobiletools.contacts.helpers.ContactsHelper | import com.simplemobiletools.contacts.helpers.ContactsHelper | ||||||
| import com.simplemobiletools.contacts.helpers.VcfExporter | import com.simplemobiletools.contacts.helpers.VcfExporter | ||||||
| @@ -56,7 +56,7 @@ fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (new | |||||||
|         sources.forEachIndexed { index, account -> |         sources.forEachIndexed { index, account -> | ||||||
|             var publicAccount = account |             var publicAccount = account | ||||||
|             if (account == config.localAccountName) { |             if (account == config.localAccountName) { | ||||||
|                 publicAccount = getString(com.simplemobiletools.contacts.R.string.phone_storage) |                 publicAccount = getString(R.string.phone_storage) | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             items.add(RadioItem(index, publicAccount)) |             items.add(RadioItem(index, publicAccount)) | ||||||
| @@ -73,7 +73,7 @@ fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (new | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fun SimpleActivity.getPublicContactSource(source: String) = if (source == config.localAccountName) getString(com.simplemobiletools.contacts.R.string.phone_storage) else source | fun SimpleActivity.getPublicContactSource(source: String) = if (source == config.localAccountName) getString(R.string.phone_storage) else source | ||||||
|  |  | ||||||
| fun BaseSimpleActivity.shareContacts(contacts: ArrayList<Contact>) { | fun BaseSimpleActivity.shareContacts(contacts: ArrayList<Contact>) { | ||||||
|     val file = getTempFile() |     val file = getTempFile() | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ const val LOCAL_ACCOUNT_TYPE = "local_account_type" | |||||||
| const val ON_CONTACT_CLICK = "on_contact_click" | const val ON_CONTACT_CLICK = "on_contact_click" | ||||||
|  |  | ||||||
| const val CONTACT_ID = "contact_id" | const val CONTACT_ID = "contact_id" | ||||||
|  | const val SMT_PRIVATE = "smt_private" | ||||||
|  |  | ||||||
| // contact photo changes | // contact photo changes | ||||||
| const val PHOTO_ADDED = 1 | const val PHOTO_ADDED = 1 | ||||||
|   | |||||||
| @@ -227,7 +227,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getContactSources(callback: (ArrayList<ContactSource>) -> Unit) { |     fun getContactSources(callback: (ArrayList<ContactSource>) -> Unit) { | ||||||
|         val sources = HashSet<ContactSource>() |         val sources = LinkedHashSet<ContactSource>() | ||||||
|         Thread { |         Thread { | ||||||
|             val uri = ContactsContract.RawContacts.CONTENT_URI |             val uri = ContactsContract.RawContacts.CONTENT_URI | ||||||
|             val projection = arrayOf(ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE) |             val projection = arrayOf(ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE) | ||||||
| @@ -247,6 +247,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) { | |||||||
|                 cursor?.close() |                 cursor?.close() | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             sources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE)) | ||||||
|             callback(ArrayList(sources)) |             callback(ArrayList(sources)) | ||||||
|         }.start() |         }.start() | ||||||
|     } |     } | ||||||
| @@ -451,109 +452,117 @@ class ContactsHelper(val activity: BaseSimpleActivity) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun insertContact(contact: Contact): Boolean { |     fun insertContact(contact: Contact): Boolean { | ||||||
|         return try { |         return if (contact.source == SMT_PRIVATE) { | ||||||
|             val operations = ArrayList<ContentProviderOperation>() |             insertLocalContact(contact) | ||||||
|             ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).apply { |         } else { | ||||||
|                 withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.source) |  | ||||||
|                 withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, getContactSourceType(contact.source)) |  | ||||||
|                 operations.add(build()) |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // names |  | ||||||
|             ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { |  | ||||||
|                 withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) |  | ||||||
|                 withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) |  | ||||||
|                 withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName) |  | ||||||
|                 withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName) |  | ||||||
|                 withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname) |  | ||||||
|                 operations.add(build()) |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // phone numbers |  | ||||||
|             contact.phoneNumbers.forEach { |  | ||||||
|                 ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { |  | ||||||
|                     withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) |  | ||||||
|                     withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) |  | ||||||
|                     withValue(CommonDataKinds.Phone.NUMBER, it.value) |  | ||||||
|                     withValue(CommonDataKinds.Phone.TYPE, it.type) |  | ||||||
|                     operations.add(build()) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // emails |  | ||||||
|             contact.emails.forEach { |  | ||||||
|                 ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { |  | ||||||
|                     withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) |  | ||||||
|                     withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) |  | ||||||
|                     withValue(CommonDataKinds.Email.DATA, it.value) |  | ||||||
|                     withValue(CommonDataKinds.Email.TYPE, it.type) |  | ||||||
|                     operations.add(build()) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // events |  | ||||||
|             contact.events.forEach { |  | ||||||
|                 ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { |  | ||||||
|                     withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) |  | ||||||
|                     withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE) |  | ||||||
|                     withValue(CommonDataKinds.Event.START_DATE, it.value) |  | ||||||
|                     withValue(CommonDataKinds.Event.TYPE, it.type) |  | ||||||
|                     operations.add(build()) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // photo (inspired by https://gist.github.com/slightfoot/5985900) |  | ||||||
|             var fullSizePhotoData: ByteArray? = null |  | ||||||
|             var scaledSizePhotoData: ByteArray? |  | ||||||
|             if (contact.photoUri.isNotEmpty()) { |  | ||||||
|                 val photoUri = Uri.parse(contact.photoUri) |  | ||||||
|                 val bitmap = MediaStore.Images.Media.getBitmap(activity.contentResolver, photoUri) |  | ||||||
|  |  | ||||||
|                 val thumbnailSize = getThumbnailSize() |  | ||||||
|                 val scaledPhoto = Bitmap.createScaledBitmap(bitmap, thumbnailSize, thumbnailSize, false) |  | ||||||
|                 scaledSizePhotoData = bitmapToByteArray(scaledPhoto) |  | ||||||
|  |  | ||||||
|                 fullSizePhotoData = bitmapToByteArray(bitmap) |  | ||||||
|                 scaledPhoto.recycle() |  | ||||||
|                 bitmap.recycle() |  | ||||||
|  |  | ||||||
|                 ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { |  | ||||||
|                     withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) |  | ||||||
|                     withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE) |  | ||||||
|                     withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData) |  | ||||||
|                     operations.add(build()) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             val results: Array<ContentProviderResult> |  | ||||||
|             try { |             try { | ||||||
|                 results = activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) |                 val operations = ArrayList<ContentProviderOperation>() | ||||||
|             } finally { |                 ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).apply { | ||||||
|                 scaledSizePhotoData = null |                     withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.source) | ||||||
|             } |                     withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, getContactSourceType(contact.source)) | ||||||
|  |                     operations.add(build()) | ||||||
|  |                 } | ||||||
|  |  | ||||||
|             // fullsize photo |                 // names | ||||||
|             val rawId = ContentUris.parseId(results[0].uri) |                 ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { | ||||||
|             if (contact.photoUri.isNotEmpty() && fullSizePhotoData != null) { |                     withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | ||||||
|                 addFullSizePhoto(rawId, fullSizePhotoData) |                     withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) | ||||||
|             } |                     withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName) | ||||||
|  |                     withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName) | ||||||
|  |                     withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname) | ||||||
|  |                     operations.add(build()) | ||||||
|  |                 } | ||||||
|  |  | ||||||
|             // favorite |                 // phone numbers | ||||||
|             val userId = getRealContactId(rawId) |                 contact.phoneNumbers.forEach { | ||||||
|             if (userId != 0 && contact.starred == 1) { |                     ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { | ||||||
|                 val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, userId.toString()) |                         withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | ||||||
|                 val contentValues = ContentValues(1) |                         withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) | ||||||
|                 contentValues.put(ContactsContract.Contacts.STARRED, contact.starred) |                         withValue(CommonDataKinds.Phone.NUMBER, it.value) | ||||||
|                 activity.contentResolver.update(uri, contentValues, null, null) |                         withValue(CommonDataKinds.Phone.TYPE, it.type) | ||||||
|             } |                         operations.add(build()) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|             true |                 // emails | ||||||
|         } catch (e: Exception) { |                 contact.emails.forEach { | ||||||
|             activity.showErrorToast(e) |                     ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { | ||||||
|             false |                         withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | ||||||
|  |                         withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) | ||||||
|  |                         withValue(CommonDataKinds.Email.DATA, it.value) | ||||||
|  |                         withValue(CommonDataKinds.Email.TYPE, it.type) | ||||||
|  |                         operations.add(build()) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // events | ||||||
|  |                 contact.events.forEach { | ||||||
|  |                     ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { | ||||||
|  |                         withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | ||||||
|  |                         withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE) | ||||||
|  |                         withValue(CommonDataKinds.Event.START_DATE, it.value) | ||||||
|  |                         withValue(CommonDataKinds.Event.TYPE, it.type) | ||||||
|  |                         operations.add(build()) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // photo (inspired by https://gist.github.com/slightfoot/5985900) | ||||||
|  |                 var fullSizePhotoData: ByteArray? = null | ||||||
|  |                 var scaledSizePhotoData: ByteArray? | ||||||
|  |                 if (contact.photoUri.isNotEmpty()) { | ||||||
|  |                     val photoUri = Uri.parse(contact.photoUri) | ||||||
|  |                     val bitmap = MediaStore.Images.Media.getBitmap(activity.contentResolver, photoUri) | ||||||
|  |  | ||||||
|  |                     val thumbnailSize = getThumbnailSize() | ||||||
|  |                     val scaledPhoto = Bitmap.createScaledBitmap(bitmap, thumbnailSize, thumbnailSize, false) | ||||||
|  |                     scaledSizePhotoData = bitmapToByteArray(scaledPhoto) | ||||||
|  |  | ||||||
|  |                     fullSizePhotoData = bitmapToByteArray(bitmap) | ||||||
|  |                     scaledPhoto.recycle() | ||||||
|  |                     bitmap.recycle() | ||||||
|  |  | ||||||
|  |                     ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { | ||||||
|  |                         withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | ||||||
|  |                         withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE) | ||||||
|  |                         withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData) | ||||||
|  |                         operations.add(build()) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 val results: Array<ContentProviderResult> | ||||||
|  |                 try { | ||||||
|  |                     results = activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) | ||||||
|  |                 } finally { | ||||||
|  |                     scaledSizePhotoData = null | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // fullsize photo | ||||||
|  |                 val rawId = ContentUris.parseId(results[0].uri) | ||||||
|  |                 if (contact.photoUri.isNotEmpty() && fullSizePhotoData != null) { | ||||||
|  |                     addFullSizePhoto(rawId, fullSizePhotoData) | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // favorite | ||||||
|  |                 val userId = getRealContactId(rawId) | ||||||
|  |                 if (userId != 0 && contact.starred == 1) { | ||||||
|  |                     val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, userId.toString()) | ||||||
|  |                     val contentValues = ContentValues(1) | ||||||
|  |                     contentValues.put(ContactsContract.Contacts.STARRED, contact.starred) | ||||||
|  |                     activity.contentResolver.update(uri, contentValues, null, null) | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 true | ||||||
|  |             } catch (e: Exception) { | ||||||
|  |                 activity.showErrorToast(e) | ||||||
|  |                 false | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private fun insertLocalContact(contact: Contact): Boolean { | ||||||
|  |         return true | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private fun addFullSizePhoto(contactId: Long, fullSizePhotoData: ByteArray) { |     private fun addFullSizePhoto(contactId: Long, fullSizePhotoData: ByteArray) { | ||||||
|         val baseUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, contactId) |         val baseUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, contactId) | ||||||
|         val displayPhotoUri = Uri.withAppendedPath(baseUri, ContactsContract.RawContacts.DisplayPhoto.CONTENT_DIRECTORY) |         val displayPhotoUri = Uri.withAppendedPath(baseUri, ContactsContract.RawContacts.DisplayPhoto.CONTENT_DIRECTORY) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user