借助本次与h5的活动联调需求,梳理下从查询日历权限->权限申请→完成插入反馈的整个链路
兼容版本权限配置包括两部分
1、清单文件
<!-- 日历读写权限 -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" /> |
2、动态权限申请
沿用项目中原有方法
xxx.checkPermission(arrayOf(
Manifest.permission.WRITE_CALENDAR, Manifest.permission.READ_CALENDAR
), object : XXCallBack() {
override fun onAllGranted(allRequestedPermissions: Array<String>) {
if (allRequestedPermissions.size == 2
&& allRequestedPermissions[0] == Manifest.permission.WRITE_CALENDAR
&& allRequestedPermissions[1] == Manifest.permission.READ_CALENDAR
) {
// 这里进行日历行程插入操作
}
}
...
}) |
日历插入行程
1、检查日历账户
存在账户的前提下在进行插入操作,无则需要手动创建一个
/**
* 检查是否存在现有账户,存在则返回账户id,否则返回-1
*/
@WorkerThread
private fun checkCalendarAccount(context: Context): Int {
val cursor: Cursor? =
context.contentResolver.query(Uri.parse(CALENDER_URL), null, null, null, null)
return cursor.use { userCursor ->
if (userCursor == null) { //查询返回空值
return -1
}
val count: Int = userCursor.count
if (count > 0) { //存在现有账户,取第一个账户的id返回
userCursor.moveToFirst()
userCursor.getInt(userCursor.getColumnIndex(CalendarContract.Calendars._ID))
} else {
-1
}
}
}
/**
* 添加日历账户,账户创建成功则返回账户id,否则返回-1
*/
@WorkerThread
private fun addCalendarAccount(context: Context): Long {
val timeZone: TimeZone = TimeZone.getDefault()
val value = ContentValues()
value.put(CalendarContract.Calendars.NAME, CALENDARS_NAME) // 存储的列名
value.put(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME) // 名称,与类型成对对应
value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE) // 类型
value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,
CALENDARS_ACCOUNT_NAME) // 日历的显示名称
value.put(CalendarContract.Calendars.OWNER_ACCOUNT, CALENDARS_ACCOUNT_NAME) // 日历所有者的账户
...
val result: Uri? = context.contentResolver.insert(calendarUri, value)
return if (result == null) -1 else ContentUris.parseId(result)
} |
2、插入行程
1、首先插入事件event
val events = ContentValues()
events.put(CalendarContract.Instances.TITLE, calendarBean.title) // 标题
events.put(CalendarContract.Instances.DESCRIPTION, calendarBean.desc) // 描述
events.put(CalendarContract.Instances.CALENDAR_ID, calId) //插入账户的id
events.put(CalendarContract.Instances.DTSTART, calendarBean.start ?: 0) //设置开始时间
events.put(CalendarContract.Instances.DTEND, calendarBean.end ?: 0) // 设置终止时间
events.put(CalendarContract.Instances.HAS_ALARM, 1) //设置有闹钟提醒
Pug.print(TAG, "TimeZone.getDefault()==${TimeZone.getDefault().id}")
events.put(CalendarContract.Instances.EVENT_TIMEZONE,
TimeZone.getDefault().id) //这个是时区,必须有
ContentProviderOperation.newInsert(Uri.parse(CALENDER_EVENT_URL))
.withValues(events)
.build() |
2、根据插入event返回的对应的eventId插入提醒行程
val reminders = ContentValues()
reminders.put(CalendarContract.Reminders.EVENT_ID, ContentUris.parseId(result.uri))
reminders.put(CalendarContract.Reminders.MINUTES, 0) // 提前previousDate天有提醒
reminders.put(CalendarContract.Reminders.METHOD,
CalendarContract.Reminders.METHOD_ALERT)
ContentProviderOperation.newInsert(Uri.parse(CALENDER_REMINDER_URL))
.withValues(reminders)
.build() |
注:如果批量插入的事件字段完全相同可以使用:CalendarContract.Events.RRULE定义重复规则
这里提醒事件可能h5会传递多个,所以选用了ContentProviderOperation做ContentProvider的批量插入,减少执行事务次数。
3、整体插入代码
/**
* 添加日历事件
*/
@WorkerThread
fun addCalendarEvent(
context: Context,
list: List<CalendarBean>,
): Boolean {
val calId = checkAndAddCalendarAccount(context) //获取日历账户的id
if (calId < 0) { //获取账户id失败直接返回,添加日历事件失败
return false
}
// 1、插入event事件 2、根据插入的事件id插入行程 3、根据最终的插入结果数返回是否完成此次插入任务
val bulkInsertNumber = list.map { calendarBean ->
val events = ContentValues()
events.put(CalendarContract.Instances.TITLE, calendarBean.title) // 标题
events.put(CalendarContract.Instances.DESCRIPTION, calendarBean.desc) // 描述
events.put(CalendarContract.Instances.CALENDAR_ID, calId) //插入账户的id
events.put(CalendarContract.Instances.DTSTART, calendarBean.start ?: 0) //设置开始时间
events.put(CalendarContract.Instances.DTEND, calendarBean.end ?: 0) // 设置终止时间
events.put(CalendarContract.Instances.HAS_ALARM, 1) //设置有闹钟提醒
Pug.print(TAG, "TimeZone.getDefault()==${TimeZone.getDefault().id}")
events.put(CalendarContract.Instances.EVENT_TIMEZONE,
TimeZone.getDefault().id) //这个是时区,必须有
ContentProviderOperation.newInsert(Uri.parse(CALENDER_EVENT_URL))
.withValues(events)
.build()
}.let { opsEvent ->
context.contentResolver.applyBatch(CalendarContract.AUTHORITY,
opsEvent as ArrayList<ContentProviderOperation>).map { result ->
val reminders = ContentValues()
reminders.put(CalendarContract.Reminders.EVENT_ID, ContentUris.parseId(result.uri))
reminders.put(CalendarContract.Reminders.MINUTES, 0) // 提前previousDate天有提醒
reminders.put(CalendarContract.Reminders.METHOD,
CalendarContract.Reminders.METHOD_ALERT)
ContentProviderOperation.newInsert(Uri.parse(CALENDER_REMINDER_URL))
.withValues(reminders)
.build()
}.let { opsReminds ->
context.contentResolver.applyBatch(CalendarContract.AUTHORITY,
opsReminds as ArrayList<ContentProviderOperation>)
}
}
return bulkInsertNumber != null && bulkInsertNumber.size == list.size
} 官方文档 |