Practical operation: MVVM implements Android address book function

Table of contents

1 Permission: Access Contacts

2 Realization: read and use address book

2.1 View layer: lazy loading ViewModel in Activity

2.2 ViewModel layer: define access to Model layer methods and LiveData

2.3 Model layer: define data class Bean and Model implementation

2.3.1 Data Bean: Contact Information

2.3.2 Model obtains all contact data of the mobile phone

3 Other business functions

3.1 Jump to the system SMS page

3.2 Get the contact's avatar


1 Permission: Access Contacts

        First, on Android 6.0 and above, you need to declare the permission to read the address book in the manifest, and dynamically monitor and apply for permission in the code.

android.Manifest.permission 

public static final String READ_CONTACTS = "android.permission.READ_CONTACTS"

2 Realization: read and use address book

2.1 View layer: lazy loading ViewModel in Activity

    private val viewModel by lazy {
        ViewModelProvider(this).get(InviteFriendsViewModel::class.java)
    }

    private fun initViewModel() {
        viewModel.readContacts(contentResolver)
        
        viewModel.getContactsListLiveData.observe(this) { contactsList ->
            clearLoadingAnimation()

            if (contactsList.isEmpty()) {
                showEmptyView()
                return@observe
            }
            adapter.setNewData(contactsList)
        }
    }

2.2 ViewModel layer: define access to Model layer methods and LiveData

class InviteFriendsViewModel : ViewModel() {

    val getContactsListLiveData by lazy {
        MutableLiveData<MutableList<ContactInfoBean>>()
    }

    fun readContacts(resolver: ContentResolver) {
        viewModelScope.launch {
            getContactsListLiveData.value = ContactsUtils.getContacts(resolver)
        }
    }

}

2.3 Model layer: define data class Bean and Model implementation

2.3.1 Data Bean: Contact Information

    data class ContactInfoBean(
        var id: String = "",
        var sortKey: String = "",
        var name: String,
        var phone: String
    )

        The contact information fields are all obtained from the Android contact database, and the field annotations are as follows:

  1. id: As the unique identifier of a contact, it can be used when identifying, updating a contact or obtaining a contact's avatar;
  2. sortKey: The read contacts are sorted and grouped by name from A->Z, which can be used when sorting contacts;
  3. name: the name of the contact, which can be used when displaying the contact;
  4. number: The mobile phone number of the contact, which can display the mobile phone number or jump to the SMS page, etc.

2.3.2 Model obtains all contact data of the mobile phone

object ContactsUtils {

    /**
     * 已自测验证:会拿到手机所有联系人数据。包括:手机联系人、SIM卡联系人(多卡联系人也能拿到数据)
     * 另外:联系人返回数据顺序根据手机厂商不同而有些许差异。如:
     *      华为P9:1、不能识别的字母;2、emoji;3、数字大小;4、A-Z字母数字。
     *      三星Note10:1、不能识别的字母;2、emoji;3、A-Z字母数字;4、数字大小。
     */
    private val KEY_PHONE_CONTACTS_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI

    suspend fun getContacts(resolver: ContentResolver): MutableList<ContactInfoBean> =
        withContext(Dispatchers.IO) { getPhoneContacts(resolver, KEY_PHONE_CONTACTS_URI) }

    /**
     * 获取手机所有联系人数据。包括:手机联系人、SIM卡联系人(多卡联系人也能拿到数据)
     */
    @SuppressLint("Range")
    suspend fun getPhoneContacts(
        resolver: ContentResolver, uri: Uri
    ): MutableList<ContactInfoBean> =
        withContext(Dispatchers.IO) {
            val contacts: MutableList<ContactInfoBean> = mutableListOf()
            var cursor: Cursor? = null
            try {
                // 在这里我们给query传递进去一个SORT_KEY_PRIMARY。 告诉ContentResolver获得的结果安装联系人名字的首字母有序排列。
                val projection = arrayOf("contact_id", "sort_key", "display_name", "data1")
                cursor = resolver.query(uri, projection, null, null, "sort_key")
                if (cursor != null) {
                    while (cursor.moveToNext()) {
                        // 联系人ID
                        val id = cursor.getString(cursor.getColumnIndex("contact_id"))
                        // Sort Key,读取的联系人按照姓名从 A->Z 排序分组。
                        val sortKey =
                            getSortKey(cursor.getString(cursor.getColumnIndex("sort_key")))
                        // 获取联系人姓名
                        val name: String = cursor.getString(cursor.getColumnIndex("display_name"))
                        // 获取联系人手机号
                        val number: String = cursor.getString(cursor.getColumnIndex("data1"))

                        if (name.isNotEmpty() && number.isNotEmpty()) {
                            contacts.add(ContactInfoBean(id, sortKey, name, number))
                        }
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                cursor?.close()
            }
            contacts
        }

    /**
     * 获取sort key的首个字符,如果是英文字母就直接返回,否则返回#。
     */
    private fun getSortKey(sortKeyString: String): String {
        val key = sortKeyString.substring(0, 1).uppercase(Locale.getDefault())
        return if (key.matches(Regex("[A-Z]"))) {
            key
        } else {
            "#"
        }
    }

}

3 Other business functions

3.1 Jump to the system SMS page

    fun gotoSystemSMSPage(context: Context, content: String = "", number: String = "") {
        Intent.ACTION_SENDTO
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("smsto:"))
        // 设置发送的内容
        intent.putExtra("sms_body", content)
        // 没有电话号码的话为默认的,即显示的时候是为空的
        intent.putExtra("address", number)
        intent.type = "vnd.android-dir/mms-sms"
        context.startActivity(intent)
    }

3.2 Get the contact's avatar

    /**
     * 获取联系人头像
     */
    fun getContactsIcon(resolver: ContentResolver, contactsId: Int): Bitmap? {
        return BitmapFactory.decodeStream(
            ContactsContract.Contacts.openContactPhotoInputStream(
                resolver, Uri.withAppendedPath(
                    ContactsContract.Contacts.CONTENT_URI, contactsId.toString() + ""
                )
            )
        )
    }

Guess you like

Origin blog.csdn.net/Agg_bin/article/details/128903020