Android,Contacts Contract:存储和检索自定义数据

Posted

技术标签:

【中文标题】Android,Contacts Contract:存储和检索自定义数据【英文标题】:Android, Contacts Contract: store and retrieve custom data 【发布时间】:2021-12-19 21:43:49 【问题描述】:

为了了解 androids ContacsContract 的工作原理,我尝试向联系人添加自定义条目。之后阅读文档、教程和观看 youtube 视频一整天,我仍然没有进一步完成这项任务。

我得到的最接近的是这个文件https://developer.android.com/reference/kotlin/android/provider/ContactsContract.Data 说明

例如,如果您将“最喜欢的歌曲”的数据行添加到 Google 帐户拥有的原始联系人,它不会同步到服务器,因为 Google 同步适配器不会知道如何处理这种数据类型。因此,新数据类型通常与新帐户类型(即新同步适配器)一起引入。

他们写了我正在尝试的内容,但遗憾的是没有提供如何完成此任务的解决方案。如果有人能提供一个简单的示例,将喜欢的歌曲的数据行添加到联系人并通过代码检索它,那就太好了

__

我自己得到的:

一种获取基本联系信息的方法:

import android.content.ContentResolver
import android.database.Cursor
import android.provider.ContactsContract

fun fetchContacts(resolver: ContentResolver) : MutableList<ItemContact> 
    var cols = listOf<String>(
        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Phone.NUMBER,
        ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
        ContactsContract.CommonDataKinds.Phone._ID,
    ).toTypedArray()
    var cursor : Cursor? = resolver.query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            cols, null, null,
            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
    )
    var contactsList : MutableList<ItemContact> 
            = emptyList<ItemContact>().toMutableList()
    if (cursor != null && cursor.count > 0) 
        while(cursor.moveToNext())
            contactsList.add(ItemContact(
                name = cursor.getString(0),
                number = cursor.getString(1),
                contact_id = cursor.getString(2),
            ))
        
    
    return contactsList

及其对应的数据类

data class ItemContact (
    val name: String,
    val number: String,
    val contact_id: String,
)

根据我目前的理解,我需要向给定的联系人添加一个新的 RawContact,代表我的应用程序,并将最喜欢的歌曲添加为单个数据条目,同时创建它。此 RawContact 应该能够使用检索到的contact_id 连接到联系人。然后我需要检查是否存在代表我的应用程序的 RawContact 用于联系人,如果存在,我将能够检索存储的歌曲,否则我在 UI 中留下占位符文本,仍然需要选择歌曲。不知何故,这涉及到一个自定义 Mimetype,但我仍然不确定这是什么以及如何创建一个。

【问题讨论】:

【参考方案1】:

首先,让我们回顾一下 ContactsContract DB 的组织方式:

    联系人表 - 每个联系人包含一行,但几乎没有任何信息 RawContacts 表 - 可以有多行,每行分配给一个联系人 ID(来自上一个表),包含多个数据行的逻辑组,通常用于单个 SyncProvider,例如 Google。 数据表 - 包含 RawContact 的实际数据,每行都有一个 MIMETYPE 列,说明该行的数据类型(电话、电子邮件、姓名等)+ 15 个数据列来保存信息本身。

还有一些伪表,例如您在代码中查询的ContactsContract.CommonDataKinds.Phone,它基本上查询数据表,但具有特定的 MIMETYPE 值,例如Phone.CONTENT_ITEM_TYPE

如果您想实现自己的 SyncProvider,您通常会创建自己的 RawContact 行,将其添加到现有的 Contact_ID 并使用新的 RAW_CONTACT_ID 添加数据行。

然后设备上的 People/Contacts 应用程序,当他们想要呈现有关某个联系人的数据时,将获取它(包括您的)所有 RawContacts 的列表,然后获取所有这些 RawContacts 的所有数据。

如果您只想向现有联系人添加一小段自定义数据,例如向联系人添加“最喜欢的歌曲”,则无需为此创建新的 RawContact,而是可以创建新的数据行并使用自定义 MIMETYPE 将其附加到现有 RawContact,但请记住,您的项目不会同步到 Google 的服务器,但仍可以在本地设备上使用。

我假设您正在关注第二个选项,所以这里有一个示例代码(未经测试):

// you need to get a RawContact ID of the contact you want to add info to
fun addFavoriteSong(context: Context, rawContactId: Long) 
    val resolver = context.contentResolver
    val ops = ArrayList<ContentProviderOperation>()
    
    ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI, true)
                .withValue(Data.RAW_CONTACT_ID, rawContactId)
                .withValue(Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.example.favorite_song")
                .withValue(Data.DATA1, "Paranoid Android")
                .withValue(Data.DATA2, "Radiohead")
                .withValue(Data.DATA3, "OK Computer")
                .build())

    try 
        val results = resolver.applyBatch(ContactsContract.AUTHORITY, ops)
        if (results.isEmpty())
            return
     catch (e: Exception) 
        e.printStackTrace()
    

    Log.i("Songs Added", "success!");

然后查询该信息以及姓名和电话等其他信息:

fun fetchContacts(resolver: ContentResolver) : MutableCollection<ItemContact> 
    var cols = arrayOf(
        Data.CONTACT_ID,
        Data.MIMETYPE,
        Data.DISPLAY_NAME,
        Phone.NUMBER,
        Data.DATA1,
        Data.DATA2,
        Data.DATA3,
    )
    
    // get only rows of MIMETYPE phone and your new custom MIMETYPE
    var selection = Data.MIMETYPE + " IN (" + Phone.CONTENT_ITEM_TYPE + ", " + "vnd.android.cursor.item/vnd.com.example.favorite_song" + ")"

    var cursor : Cursor? = resolver.query(Data.CONTENT_URI, cols, null, null, Data.CONTACT_ID)
    val map = hashMapOf<Long, ItemContact>()
    
    while(cursor != null && cursor.moveToNext()) 
        val contactId = cursor.getLong(0)
        val mimetype = cursor.getString(1)

        // gets the existing ItemContact from the map or if not found, puts an empty one in the map
        val contact = map.getOrPut(contactId)  ItemContact() 

        with(contact) 
            if (mimetype == Phone.CONTENT_ITEM_TYPE) 
                contact_id = contactId
                name = cursor.getString(2)
                number = cursor.getString(3)
             else 
                contact_id = contactId
                song = cursor.getString(4)
                band = cursor.getString(5)
                album = cursor.getString(6)
            
        
    
    return map.values

【讨论】:

谢谢,Mimetype 是否有任何限制/约定,或者我可以使用我喜欢的任何字符串,只要它是唯一的?

以上是关于Android,Contacts Contract:存储和检索自定义数据的主要内容,如果未能解决你的问题,请参考以下文章

Android 5.1 Contacts源码分析:Contacts模块主要窗口

Android 5.1 Contacts源码分析:Contacts模块ListView Adapter结构

Android 5.1 Contacts源码分析:Contacts模块Fargment结构

ContactsContract.Contacts.IN_VISIBLE_GROUP 在 Android 中是啥意思?

Contacts解析

contacts.find 在 Android 4 上不起作用(phonegap)