flutter对android的日历数据相关操作
2020-04-01 本文已影响0人
Magic旭
系统日历
三个不同URL的作用
- 每个url通过contentProvider能获得对应数据库的访问句柄,所以可以理解三个url对应三个数据库,如果通过一个事件关联3个库呢,下面会详细介绍。
数据库介绍参考链接
//对系统日历插入行程事件的url
private static String CALANDER_EVENT_URL = "content://com.android.calendar/events";
//此表储存日历特定信息。 此表中的每一行都包含一个日历的详细信息,例如名称、颜色、同步信息等。
private static String CALANDER_URL = "content://com.android.calendar/calendars";
//为日历中的行程事件添加提醒功能,就是通过系统通知栏告知你今天有什么行程
private static String CALENDER_REMINDER_URL = "content://com.android.calendar/reminders";
同个事件关联不同表
这里以关联行程事件表和事件提醒表为例子进行说明
- 使用为uri: content://com.android.calendar/events,日历插入一个行程事件,使用ContentProvider进行数据库插入数据时,方法会返回一个Uri。如果插入失败,这个Uri为空;如果成功,则不为空。
ContentValues event = new ContentValues();
//事件的起始时间
event.put("dtstart", startDateMis);
if (cyclerType == 0) {
//如果不是循环事件,添加事件的结束事件
event.put("dtend", endDateMis);
} else {
//如果是循环事件,添加持续事件
long durationSecondTime = (endDateMis - startDateMis) / 1000;
event.put(CalendarContract.Events.DURATION, String.format("P%dS", durationSecondTime));
}
//日历id,必须要有,可以通过**CALANDER_URL**查询获取其id
event.put("calendar_id", calId);
event.put("title", title);
event.put("description", note);
event.put("hasAlarm", 1);
event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());//这个是时区,必须有
//这里的URL是event_url,别看错了
Uri events = activity.getContentResolver().insert(Uri.parse(CALANDER_EVENT_URL), event);
- 等待insert插入数据后,拿到Uri,使用uri:content://com.android.calendar/reminders,为我们的行程事件添加一个事件提醒功能,下面我们就可以开始把当前的数据的id关联到事件提醒功能表单中。
注意:关键代码ContentUris.parseId(Uri uri),将Uri转成关联表的id。
//重新生成一个ContentValues对象,用于插入 事件提醒功能 的表单中
ContentValues values = new ContentValues();
//将上面insert插入数据生成的uri转成id,再塞到 事件提醒功能 的表单作为关联id
values.put(CalendarContract.Reminders.EVENT_ID, ContentUris.parseId(events));
//提前到什么时候进行提示
values.put(CalendarContract.Reminders.MINUTES, 15);
//提醒的方式
values.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT);
Uri uri = activity.getContentResolver().insert(Uri.parse(CALENDER_REMINDER_URL), values);
- 到这里为止,我们就成功的为系统日历添加上event
如何获取android日历读取权限
- 这篇文章重点不在于讲解flutter如何与native通信,如果想看的可以连接下Registrar类与Channel类,大概是内存块映射关系。我会抽时间总结下如何通信这块再出一篇文章。
- 着重讲下权限问题,因为在android 6.0以上日历读写权限是需要动态申请的。下面就是flutter层如何申请android权限的问题。
第一步
在android目录的mainfest文件写入需要的权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxxxxxxxx">
//读日历数据
<uses-permission android:name="android.permission.READ_CALENDAR" />
//写日历数据
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
</manifest>
第二步
在android代码中判断sdk版本,大于6.0动态申请权限
//REQUEST_CALENDAR_PERMISSION是requestCode,用来判断用户是否点击允许该权限了
public static int REQUEST_CALENDAR_PERMISSION = 10;
//ActivityCompat为androidx的包
requestCalendarAuthorization () {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_CALENDAR,
Manifest.permission.READ_CALENDAR}, REQUEST_CALENDAR_PERMISSION);
} else {
result.success(1)
}
}
第三步
- 为flutter注册方法映射入口,静态注册入口。permissions为映射句柄的名字。
问题:在这一步卡了很久,因为动态获取权限乃是要在onRequestPermissionsResult方法中才能知道用户是否点击了允许。但是我又不在不能拿到完全的activity,怎么能实现这个方法呢。后面才知道google官网已经把一切都考虑很全面了。查看Registrar类可以看到能添加各种各样的回调用来响应原生层的方法。
//android层面
@JvmStatic
fun registerWith(registrar: Registrar) {
mContext = registrar.context()
mActivity = registrar.activity()
val channel = MethodChannel(registrar.messenger(), "permissions")
//设置映射方法调用的回调,A类是实现MethodCallHandler方法的类
channel.setMethodCallHandler(A())
//设置动态获取权限的结果回调。
registrar.addRequestPermissionsResultListener { id, permissions, grantResults ->
//这里要注意通过grantResults来判断用户是同意还是拒绝权限
return if (requestCode == REQUEST_CALENDAR_PERMISSION && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mCurrentResult?.success(1)
mCurrentResult = null
true
} else {
mCurrentResult?.success(0)
mCurrentResult = null
false
}
}
}
//当映射的方法调用时候,就能在onMethodCall找到相应方法;
//call:方法名 result:用于把结果回调给flutter
override fun onMethodCall(call: MethodCall, result: Result) {
when {
call.method == "requestPermission" -> {
//记录当前方法的result引用,用于等待权限结果回来时候回调给flutter
mCurrentResult = result
//第二步的方法
requestCalendarAuthorization()
}
……
}
}
- flutter层调用
//生命调用的句柄:permissions
static const MethodChannel _channel = const MethodChannel('permissions');
//直接映射方法名,这里的result就会收到0 or 1
var result = await _channel.invokeMethod('requestPermission');
问题
- 如何能拿到Activity的onRequestPermissionsResult方法呢?
解决办法:通过Registrar类添加listener - 不在mainfest里面声明权限,只是代码动态获取,然后没有弹窗让用户选择是否允许。就是在《如何获取android日历读取权限-第一步》我跳过了,直接从第二步开始,没有权限弹窗出现。
解决办法:把第一步加上,重新跑就可以了。好像是mainfest里面如果不声明权限的话,你手机-应用权限管理:看不到日历权限这一个开关的。所以自然也不会弹窗让用户选择是否允许打开。 - 添加无限循环事件失败,日历中只循环3次就不出现日程了。
解决办法:在插入Event事件时候要区分是否是循环事件,如果是循环事件则不要tend,需要duration参数。rrule和rdate二选一即可,但是循环事件必须要duration参数,不需要tend。参考链接
//startTime起始时间,endTime终止时间。cyclerType日程事件类型
……
if (cyclerType == 0) {
//如果不是循环事件
event.put("dtend", endDateMis);
} else {
//如果是循环事件
long durationSecondTime = (endDateMis - startDateMis) / 1000;
event.put(CalendarContract.Events.DURATION, String.format("P%dS", durationSecondTime));
}