I am trying to edit contacts from my app so I implemented the following function :
private fun updateContact(contact: Contact) {
val contentProviderOperations = ArrayList<ContentProviderOperation>()
val contentValues = ContentValues().apply {
put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.first_name)
put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, contact.last_name)
put(
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
contact.first_name + " " + contact.last_name
)
}
contact.phones_list.forEach { phone ->
contentValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone.number)
contentValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, phone.type)
}
contact.emails_list.forEach { email ->
contentValues.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email.address)
contentValues.put(ContactsContract.CommonDataKinds.Email.TYPE, email.type)
}
contentProviderOperations.add(
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(
ContactsContract.Data.CONTACT_ID + "=?",
arrayOf(contact.id.toString())
)
.withValues(contentValues)
.build()
)
contentResolver.applyBatch(ContactsContract.AUTHORITY, contentProviderOperations)
}
this is my Contact data class :
data class Contact(
var id: Long,
var lookup_key: String,
var display_name: String,
var first_name: String,
var last_name: String,
var phones_list: MutableList<Phone>,
var emails_list: MutableList<Email>,
var photo_uri: Uri? = null
)
This function is supposed to edit all of the contact data(first name, last name, all phone nubmers, all emails) acrroding to its id which I already have from previously retrieving the contact.
When the function is executed it throws no exception so it seems to be working fine but when I go to the original contacts app in my phone the contact that was supposed to be edited disappears and It does not longer appear..
Any suggestions what could have gone wrong?
Thanks
Related
#Query("UPDATE RealEstateDatabase SET type = :entryType WHERE id = :id AND type NOT LIKE :entryType")
suspend fun updateRealEstate(entryType: String, id: String)
This code works perfectly to update a row in the table based on a condition I want to do the same thing with several rows each linked to a condition
here is the entity/table in question
#Entity
#Parcelize
data class RealEstateDatabase(
#PrimaryKey
var id: String,
var type: String? = null,
var price: Int? = null,
var area: Int? = null,
var numberRoom: String? = null,
var description: String? = null,
var numberAndStreet: String? = null,
var numberApartment: String? = null,
var city: String? = null,
var region: String? = null,
var postalCode: String? = null,
var country: String? = null,
var status: String? = null,
var dateOfEntry: String? = null,
var dateOfSale: String? = null,
var realEstateAgent: String? = null,
var lat: Double ?=null,
var lng: Double ?=null,
var hospitalsNear : Boolean = false,
var schoolsNear : Boolean = false,
var shopsNear : Boolean = false,
var parksNear : Boolean = false,
#ColumnInfo(name = "listPhotoWithText")
var listPhotoWithText : List<PhotoWithTextFirebase> ?=null,
var count_photo : Int? = listPhotoWithText?.size,
)
I also put the method of my repository , this method has parameters of the same type as my table to modify
override suspend fun updateRealEstate(
id: String,
entryType: String,
entryPrice: String,
entryArea: String,
entryNumberRoom: String,
entryDescription: String,
entryNumberAndStreet: String,
entryNumberApartement: String,
entryCity: String,
entryRegion: String,
entryPostalCode: String,
entryCountry: String,
entryStatus: String,
textDateOfEntry: String,
textDateOfSale: String,
realEstateAgent: String?,
lat: Double?,
lng: Double?,
checkedStateHopital: MutableState<Boolean>,
checkedStateSchool: MutableState<Boolean>,
checkedStateShops: MutableState<Boolean>,
checkedStateParks: MutableState<Boolean>,
listPhotoWithText: List<PhotoWithTextFirebase>?,
itemRealEstate: RealEstateDatabase
): Response<Boolean> {
return try {
Response.Loading
val rEcollection = firebaseFirestore.collection("real_estates")
if(entryType != itemRealEstate.type ){
rEcollection.document(id).update("type",entryType)
}
realEstateDao.updateRealEstate(entryType,id)
Response.Success(true)
}catch (e: Exception) {
Response.Failure(e)
}
}
I repeat that I want to update the rows of the table on the condition that the variable given by the method of my repo is different from the row in question
#Query("UPDATE RealEstateDatabase SET " +
"type = (CASE WHEN type NOT LIKE :entryType THEN (:entryType) ELSE type END) ," +
"price = (CASE WHEN price NOT LIKE :entryPrice THEN (:entryPrice) ELSE price END) WHERE id =:id")
suspend fun updateRealEstate(
entryType: String,
id: String,
entryPrice: Int
)
this solution works but forces me to update the value anyway
What you can do is add a condition in the WHERE clause which will prevent the execution of the UPDATE statement if both columns do not need to be updated:
#Query("UPDATE RealEstateDatabase SET " +
"type = CASE WHEN type NOT LIKE :entryType THEN (:entryType) ELSE type END," +
"price = CASE WHEN price NOT LIKE :entryPrice THEN (:entryPrice) ELSE price END " +
"WHERE id = :id AND (type NOT LIKE :entryType OR price NOT LIKE :entryPrice)")
I've got the following code to retrieve a contact list with Name + PhoneNumber:
#SuppressLint("Range")
fun getNamePhoneDetails(): ArrayList<List<String>>? {
val names = ArrayList<List<String>>()
val cr = contentResolver
val cur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
null, null, null)
if (cur!!.count > 0) {
while (cur.moveToNext()) {
val id = cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NAME_RAW_CONTACT_ID))
val name = cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
val number = cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
names.add(listOf(id, name, number))
}
}
return names
}
The output is correct once no contact has two phone numbers. However once any contact has two phone numbers I get the contact twice. It has the same id and name, but a different phone number. How can I make it so that the returned list does not have a contact twice but all phone numbers inside a list?
Something like
[1, Name Name, [phonenumber1, phonenumber2]],[2, Name Name, [phonenumber1]]
Then I could just iterate through the phonenumberlist and have all numbers as valid strings.
You should rather create a POJO Object and than assign the data into it and return a list of that POJO Object and then you can make use of distinct extension on the list to get a filtered result they way you want it .
This is how you can achieve what you want :
Create a POJO Object :
data class Contact(
val id : String ,
val name : String,
val number : String)
And then when retrieveing the data you can do the following :
#SuppressLint("Range")
fun getNamePhoneDetails(): MutableList<Contact>? {
val names = MutableList<Contact>()
val cr = contentResolver
val cur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
null, null, null)
if (cur!!.count > 0) {
while (cur.moveToNext()) {
val id = cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NAME_RAW_CONTACT_ID))
val name = cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
val number = cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
names.add(Contact(id , name , number))
}
}
return names
}
And then while retreiving the data you need to just filter the list in the following manner :
val list = getNamePhoneDetails()
list.distinctBy { it.number }.forEach { println(it) }
I want to fetch a list of local contacts with an anniversary date set.
I'm doing the following:
object WithAnniversary {
const val INDEX_CONTACT_ID = 0
const val INDEX_CONTACT_NAME = 1
const val INDEX_CONTACT_ANNIVERSARY = 2
const val INDEX_CONTACT_PHOTO = 3
val PROJECTION = arrayOf(
CommonDataKinds.Event.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
CommonDataKinds.Event.START_DATE,
CommonDataKinds.Phone.PHOTO_URI
)
const val WHERE = "${ContactsContract.Data.MIMETYPE} = ? AND " +
"${CommonDataKinds.Event.TYPE} = " +
"${CommonDataKinds.Event.TYPE_ANNIVERSARY}"
val SELECTION = arrayOf(CommonDataKinds.Event.CONTENT_ITEM_TYPE)
val SORT_ORDER: String? = null
}
#Throws(Exception::class)
fun obtainContactsWithAnniversaries(): List<Contact> {
val list = mutableListOf<Contact>()
val cursor = context.contentResolver.query(
ContactsContract.Data.CONTENT_URI,
WithAnniversary.PROJECTION,
WithAnniversary.WHERE,
WithAnniversary.SELECTION,
WithAnniversary.SORT_ORDER
)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getLong(WithAnniversary.INDEX_CONTACT_ID)
val name = cursor.getString(WithAnniversary.INDEX_CONTACT_NAME)
val date = cursor.getString(WithAnniversary.INDEX_CONTACT_ANNIVERSARY)
val avatarUri = cursor.getString(WithAnniversary.INDEX_CONTACT_PHOTO)
try {
val contact = contactFactory.create(id.toString(), name, null, date, avatarUri)
list.add(contact)
} catch (e: Exception) {
Log.d(TAG, "Could not parse contact with name: $name")
}
}
cursor.close()
return list.sorted()
} else {
throw Exception("Unable to retrieve contacts, returned cursor is null")
}
}
I use the exact same process for retrieving contacts with birthday dates, but using TYPE_BIRTHDAY instead of TYPE_ANNIVERSARY, but for some reason this doesn't work for anniversaries.
I have checked my local contact list and I have some contacts with birthdays and anniversaries. I can retrieve a list with contacts with birthdays but the list of contacts with anniversaries is empty.
Any help will be appreciated.
I assume the issue is with the conversion of the cursor row to your custom Contact class.
When I replace that part with just a log your code works for me:
...
while (cursor.moveToNext()) {
val id = cursor.getLong(WithAnniversary.INDEX_CONTACT_ID)
val name = cursor.getString(WithAnniversary.INDEX_CONTACT_NAME)
val date = cursor.getString(WithAnniversary.INDEX_CONTACT_ANNIVERSARY)
val avatarUri = cursor.getString(WithAnniversary.INDEX_CONTACT_PHOTO)
Log.d("TEMP", "contact $id $name $date $avatarUri")
}
...
Log:
D/TEMP: contact 98014 Test1 1979-10-06 content://com.android.contacts/contacts/98014/photo
contact 4603 test 1990-07-22 content://com.android.contacts/contacts/4603/photo
contact 98341 Voice Mail 2013-11-06 null
I have written the following query to read contacts from a device.
private fun getContactPhoneNumbers(resolver: ContentResolver): Map<String, NameAndPhoneList> {
val startTime = System.nanoTime()
val map = hashMapOf<String, NameAndPhoneList>()
val projection = arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.PHOTO_THUMBNAIL_URI
)
val selection =
"${ContactsContract.Contacts.DISPLAY_NAME} NOT LIKE '' and ${ContactsContract.Contacts.DISPLAY_NAME} NOT NULL"
resolver.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
selection,
null,
null,
null
)?.let { cursor ->
if (cursor.count > 0) {
while (cursor.moveToNext()) {
val id: String? =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
val name: String? =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
val photoUri =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))
val phone =
if (cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
val pCur: Cursor = resolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
arrayOf(id),
null
)!!
val numbers = mutableListOf<String>()
while (pCur.moveToNext()) {
val phoneNo: String = pCur.getString(
pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER
)
)
numbers.add(phoneNo)
}
pCur.close()
numbers
} else
null
// take contacts which either have email or phone numbers
if (id != null && name != null) {
map[id] = NameAndPhoneList(name, phone, photoUri?.let { Uri.parse(it) })
}
}
}
cursor.close()
}
val endTime = System.nanoTime() - startTime
Timber.i("$CONTACT_SYNC_PHONE_MAP_QUERY_TIME = $endTime")
return map
}
private data class NameAndPhoneList(
val name: String,
val phoneList: List<String>?,
val imageUri: Uri?
)
this is taking 112877872699 ns (~2 min) for a phonebook of length 6,300 contacts. Is this expected or can we optimize further?
for 6,300 contacts (assuming all have a phone) you're making 6,301 queries, which is why it's so slow...
Instead you can benefit from ContactsContract's "implicit join" feature, which allows you to get Contacts.CONTENT_URI fields when querying over the Data.CONTENT_URI table.
So, just query directly over the Data table, get all the phones including the CONTACT_ID, DISPLAY_NAME, etc. fields, and put it in some map contact-id => data.
Here's sample code in Java that can help - https://stackoverflow.com/a/44383937/819355
I am able to get user phone numbers from the contacts list, but i also need names with the numbers,
I know if i use custom adapter then i can get name and number both, but i want to use the default contact picker.
This is my code.
private fun launchMultiplePhonePicker() {
val phonebookIntent = Intent("intent.action.INTERACTION_TOPMENU")
phonebookIntent.putExtra("additional", "phone-multi")
phonebookIntent.putExtra("maxRecipientCount", 20)
phonebookIntent.putExtra("FromMMS", true)
startActivityForResult(phonebookIntent, 110)
}
This does work fine, but only returns phone numbers, and not contact names in onActivityResult.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val bundle = data?.extras
val result = bundle?.getString("result")
val contacts = bundle?.getStringArrayList("result")
}
Edit:
I found out that intent.action.INTERACTION_TOPMENU may not work in all devices, so i used the following approach, its giving me the names with number, but not allowing me to select multiple contacts.
val intent = Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
startActivityForResult(intent, 10101)
My simple solution to get contacts, may be it will help you
Data class to hold extracted values:
data class ContactModel(val phoneNumber: String, val displayName: String)
Get contacts and map to model
val result = arrayListOf<ContactModel>()
val cursor.context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, ContactsContract.Contacts.SORT_KEY_PRIMARY + " ASC")
cursor?.let {
val nameIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
val phoneIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
while (it.moveToNext()) {
val name = cursor.getString(nameIndex)
val phone = cursor.getString(phoneIndex)
var num = phone
.replace(" ", "")
.replace("-", "")
.replace("(", "")
.replace(")", "")
val contactModel = ContactModel(num, name)
//Prevents duplicated contacts on some devices
if (it.position != 0) {
if (contactModel != result[result.size - 1]) {
result.add(contactModel)
}
} else {
result.add(contactModel)
}
}
}
If you have a list of phone numbers and you need to get the display names, you can use ContactsContract.PhoneLookup, something like this:
private fun phoneToName(phone: String): String? {
val uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phone))
var cur = getContentResolver().query(uri, arrayOf(PhoneLookup.DISPLAY_NAME), null, null, null)
if (cur.moveToFirst()) {
return cur.getString(0)
}
cur.close()
return null
}
P.S. just note that "intent.action.INTERACTION_TOPMENU" is not an official Android API, and is probably not supported by all devices.
EDIT: there's no official way of using the phone-picker for multiple contacts, either you implement your own contact list and let the user choose multiple contacts within your app, or you can allow the user to pick contacts multiple times until they finish.