【问题标题】:Android, Contacts Contract: store and retrieve custom dataAndroid,Contacts Contract:存储和检索自定义数据
【发布时间】: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,但我仍然不确定这是什么以及如何创建一个。

【问题讨论】:

    标签: android kotlin android-contacts


    【解决方案1】:

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

    1. 联系人表 - 每个联系人包含一行,但几乎没有任何信息
    2. RawContacts 表 - 可以有多行,每行分配给一个联系人 ID(来自上一个表),包含多个数据行的逻辑组,通常用于单个 SyncProvider,例如 Google。
    3. 数据表 - 包含 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 是否有任何限制/约定,或者我可以使用我喜欢的任何字符串,只要它是唯一的?
    猜你喜欢
    • 2021-04-02
    • 2013-09-04
    • 1970-01-01
    • 2017-11-20
    • 2018-05-28
    • 2016-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多