目录
2.1 View层:Activity中懒加载ViewModel
2.2 ViewModel层:定义访问Model层方法以及LiveData
1 权限:访问通讯录
首先,在Android6.0及以上,需在manifest声明读取通讯录的权限,并在代码中动态监测和申请权限。
android.Manifest.permission
public static final String READ_CONTACTS = "android.permission.READ_CONTACTS"
2 实现:读取、使用通讯录
2.1 View层:Activity中懒加载ViewModel
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层:定义访问Model层方法以及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层:定义数据类Bean以及Model实现
2.3.1 数据类Bean:联系人信息
data class ContactInfoBean(
var id: String = "",
var sortKey: String = "",
var name: String,
var phone: String
)
联系人信息字段,都是从Android联系人数据库中拿到,字段注解如下:
- id:作为联系人唯一标识,可在识别、更新联系人或获取联系人头像时使用;
- sortKey:读取的联系人按照姓名从 A->Z 排序分组,可在联系人排序时使用;
- name:联系人姓名,可在显示联系人时使用;
- number:联系人手机号,可显示手机号或跳转短信页面等。
2.3.2 Model获取手机所有联系人数据
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 其他业务功能
3.1 跳转到系统短信页面
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 获取联系人头像
/**
* 获取联系人头像
*/
fun getContactsIcon(resolver: ContentResolver, contactsId: Int): Bitmap? {
return BitmapFactory.decodeStream(
ContactsContract.Contacts.openContactPhotoInputStream(
resolver, Uri.withAppendedPath(
ContactsContract.Contacts.CONTENT_URI, contactsId.toString() + ""
)
)
)
}