From 70b011a6976694c98be7578c54486791c58d2a34 Mon Sep 17 00:00:00 2001 From: tibbi Date: Tue, 26 Dec 2017 19:51:48 +0100 Subject: [PATCH] properly handle photo adding and removing at contact update --- app/build.gradle | 2 +- .../contacts/activities/ContactActivity.kt | 24 ++++-- .../contacts/adapters/ContactsAdapter.kt | 2 + .../contacts/helpers/Config.kt | 1 + .../contacts/helpers/Constants.kt | 7 +- .../contacts/helpers/ContactsHelper.kt | 79 ++++++++++++++----- 6 files changed, 90 insertions(+), 25 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 56a6988d..a494e962 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ ext { } dependencies { - implementation 'com.simplemobiletools:commons:3.4.2' + implementation 'com.simplemobiletools:commons:3.4.5' debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion" diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ContactActivity.kt index 5ec04f0d..41c10f5c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ContactActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ContactActivity.kt @@ -34,8 +34,7 @@ import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.sendEmailIntent import com.simplemobiletools.contacts.extensions.sendSMSIntent import com.simplemobiletools.contacts.extensions.tryStartCall -import com.simplemobiletools.contacts.helpers.CONTACT_ID -import com.simplemobiletools.contacts.helpers.ContactsHelper +import com.simplemobiletools.contacts.helpers.* import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Email import com.simplemobiletools.contacts.models.PhoneNumber @@ -327,6 +326,8 @@ class ContactActivity : SimpleActivity() { } contact!!.apply { + val oldPhotoUri = photoUri + firstName = contact_first_name.value middleName = contact_middle_name.value surname = contact_surname.value @@ -339,7 +340,8 @@ class ContactActivity : SimpleActivity() { if (id == 0) { insertNewContact() } else { - updateContact() + val photoUpdateStatus = getPhotoUpdateStatus(oldPhotoUri, photoUri) + updateContact(photoUpdateStatus) } }.start() } @@ -384,15 +386,27 @@ class ContactActivity : SimpleActivity() { } } - private fun updateContact() { + private fun updateContact(photoUpdateStatus: Int) { isSaving = true - if (ContactsHelper(this@ContactActivity).updateContact(contact!!)) { + if (ContactsHelper(this@ContactActivity).updateContact(contact!!, photoUpdateStatus)) { finish() } else { toast(R.string.unknown_error_occurred) } } + private fun getPhotoUpdateStatus(oldUri: String, newUri: String): Int { + return if (oldUri.isEmpty() && newUri.isNotEmpty()) { + PHOTO_ADDED + } else if (oldUri.isNotEmpty() && newUri.isEmpty()) { + PHOTO_REMOVED + } else if (oldUri != newUri) { + PHOTO_CHANGED + } else { + PHOTO_UNCHANGED + } + } + private fun addNewPhoneNumberField() { layoutInflater.inflate(R.layout.item_phone_number, contact_numbers_holder, false).apply { updateTextColors(this as ViewGroup) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt index 84917ba0..22225667 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt @@ -8,6 +8,7 @@ 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 import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.extensions.getColoredDrawableWithColor @@ -118,6 +119,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: MutableList) -> Unit) { val accounts = HashSet() @@ -74,7 +73,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) { cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, sortOrder) if (cursor?.moveToFirst() == true) { do { - val id = cursor.getIntValue(ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID) + val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID) val firstName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) ?: "" val middleName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME) ?: "" val surname = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) ?: "" @@ -192,7 +191,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) { val uri = ContactsContract.Data.CONTENT_URI val projection = getContactProjection() - val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID} = ?" + val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?" val selectionArgs = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id.toString()) var cursor: Cursor? = null try { @@ -215,7 +214,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) { } private fun getContactProjection() = arrayOf( - ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID, + ContactsContract.Data.RAW_CONTACT_ID, ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, @@ -238,11 +237,12 @@ class ContactsHelper(val activity: BaseSimpleActivity) { return sort } - fun updateContact(contact: Contact): Boolean { + fun updateContact(contact: Contact, photoUpdateStatus: Int): Boolean { return try { + activity.toast(R.string.updating) val operations = ArrayList() ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).apply { - val selection = "${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?" + val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?" val selectionArgs = arrayOf(contact.id.toString(), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) withSelection(selection, selectionArgs) withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName) @@ -289,6 +289,12 @@ class ContactsHelper(val activity: BaseSimpleActivity) { } } + // photo + when (photoUpdateStatus) { + PHOTO_ADDED -> addPhoto(contact, operations) + PHOTO_REMOVED -> removePhoto(contact, operations) + PHOTO_CHANGED -> {} + } activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) true } catch (e: Exception) { @@ -297,6 +303,48 @@ class ContactsHelper(val activity: BaseSimpleActivity) { } } + private fun addPhoto(contact: Contact, operations: ArrayList): ArrayList { + 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) + val scaledSizePhotoData = bitmapToByteArray(scaledPhoto) + scaledPhoto.recycle() + + val fullSizePhotoData = bitmapToByteArray(bitmap) + bitmap.recycle() + + bitmap.recycle() + ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { + withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id) + withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, scaledSizePhotoData) + operations.add(this.build()) + } + + val baseUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, contact.id.toLong()) + val displayPhotoUri = Uri.withAppendedPath(baseUri, ContactsContract.RawContacts.DisplayPhoto.CONTENT_DIRECTORY) + val fileDescriptor = activity.contentResolver.openAssetFileDescriptor(displayPhotoUri, "rw") + val photoStream = fileDescriptor.createOutputStream() + photoStream.write(fullSizePhotoData) + photoStream.close() + fileDescriptor.close() + } + return operations + } + + 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(), ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + withSelection(selection, selectionArgs) + operations.add(this.build()) + } + return operations + } + fun insertContact(contact: Contact): Boolean { return try { activity.toast(R.string.inserting) @@ -373,16 +421,11 @@ class ContactsHelper(val activity: BaseSimpleActivity) { val rawContactId = ContentUris.parseId(results[0].uri) val baseUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId) val displayPhotoUri = Uri.withAppendedPath(baseUri, ContactsContract.RawContacts.DisplayPhoto.CONTENT_DIRECTORY) - val photoStream = activity.contentResolver.openAssetFileDescriptor(displayPhotoUri, "rw").createOutputStream() - photoStream.use { - var bufferSize = 16 * 1024 - var offset = 0 - while (offset < fullSizePhotoData.size) { - bufferSize = Math.min(bufferSize, fullSizePhotoData.size - offset) - photoStream.write(fullSizePhotoData, offset, bufferSize) - offset += bufferSize - } - } + val fileDescriptor = activity.contentResolver.openAssetFileDescriptor(displayPhotoUri, "rw") + val photoStream = fileDescriptor.createOutputStream() + photoStream.write(fullSizePhotoData) + photoStream.close() + fileDescriptor.close() } true @@ -434,10 +477,10 @@ class ContactsHelper(val activity: BaseSimpleActivity) { } private fun getThumbnailSize(): Int { + val uri = ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI + val projection = arrayOf(ContactsContract.DisplayPhoto.THUMBNAIL_MAX_DIM) var cursor: Cursor? = null try { - val uri = ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI - val projection = arrayOf(ContactsContract.DisplayPhoto.THUMBNAIL_MAX_DIM) cursor = activity.contentResolver.query(uri, projection, null, null, null) if (cursor?.moveToFirst() == true) { return cursor.getIntValue(ContactsContract.DisplayPhoto.THUMBNAIL_MAX_DIM)