内容提供者-向日历中插入提醒事件
比如说你要写一个购物,卖票的软件,那么用户可以设置抢票提醒:
使用内容提供者,我们向日历里插入提醒事件,比如说什么时候要抢购商品呀,家人的生日呀,小孩上学,纪念日之类的。
今天呢我们也来实现一下这个功能!我们向日历里插入提醒事件。
官方文档地址:
从前面掌握的知识进行思考
如果我们要操作日历的内容提供者,需要什么条件? - 先拿到contentResolver吧,接着要有Uri吧? - 获取URI - 知道各张表的有保存的信息
找到authory
怎么知道它的URI是什么呢?可以看文档,也可以看源码:
android上层应用的源码在这:
路径如下:
搜索:
static {
也就是说,authorty是:CalendarContract.AUTHORITY
那我们就找这个常量:
同学们如果不下载源码的话,可以在这里搜索
https://androidxref.com/7.1.1_r6/xref/frameworks/base/core/java/android/provider/CalendarContract.java
所以,这个常量是:
/**
* This authority is used for writing to or querying from the calendar
* provider. Note: This is set at first run and cannot be changed without
* breaking apps that access the provider.
*/
public static final String AUTHORITY = "com.android.calendar";
那么,咱们的URI,就有了,剩下的,就是看是我们要操作哪张表了。
找到path
以上这些呢,就是对应的表,有什么表请看上面的截图吧!
权限
前面我们有日历内容提供者的源码,我们去看看它注册的地方:
<provider android:name="CalendarProvider2" android:authorities="com.android.calendar"
android:label="@string/provider_label"
android:multiprocess="false"
android:exported="true"
android:readPermission="android.permission.READ_CALENDAR"
android:writePermission="android.permission.WRITE_CALENDAR" />
由上可知道,它需要读写日历的权限
所以我们要在配置文件上添加以下代码
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
别急,到这里没完呢。权限我们得考虑6.0以上的版本,也就是api23以上的版本
其实写博客挺麻烦的哈,希望同学们能学到东西吧。
在android api23以年的版本我们需要动态地去获取权限。6.0以下的,声明,安装时会提示用户。
怎么动态获取呢?
就看下面的代码吧,我写一起了,如果不懂的话,后面看视频哈。
package com.sunofbeaches.calendarproviderdemo;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@RequiresApi(api = Build.VERSION_CODES.M)
public class MainActivity extends AppCompatActivity {
public static final int PERMISSION_RESULT_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkCalendarPermission();
}
private void checkCalendarPermission() {
int writeResult = checkSelfPermission(Manifest.permission.WRITE_CALENDAR);
int readResult = checkSelfPermission(Manifest.permission.READ_CALENDAR);
if(writeResult != PackageManager.PERMISSION_GRANTED || readResult != PackageManager.PERMISSION_GRANTED) {
//没有权限,需要申请权限
//一般先提示用户,如果用户点击了确定,才去请求权限
//TODO:同学们就展示一个新的界面,然后如果用户点击不再显示,则不要再显示请求权限了,也不要给用户使用程序,因为没意义呀。
//TODO:这里我就直接上检查权限的代码,在视频里我们会按实际项目流程走,把提示也做出来。
//请求权限
requestPermissions(new String[]{Manifest.permission.WRITE_CALENDAR,Manifest.permission.READ_CALENDAR},PERMISSION_RESULT_CODE);
} else {
//有权限
}
}
@Override
public void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults) {
//权限请求结果
if(requestCode == PERMISSION_RESULT_CODE) {
// permissions,这个就是前面我们传的数组,请求权限的数组
// grantResults 结果
if(grantResults.length == 2 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//有权限,不用做操作
} else {
// 无权限,结束程序
}
}
}
}
表结构
如果没有root的手机我们是不是拿不出表来看呀,那怎么办呢?
authory知道了,表名也知道了,那么我们就随便查询呗,把里面的字段都打出来不就完事了吗?
Uri uri = Uri.parse("content://" + CalendarContract.AUTHORITY + "/calendars");
其实呀,有常量的,所以不需要自己拼接。
Uri uri = CalendarContract.Calendars.CONTENT_URI;
通过代码获取到表的字段
//测试表结构
ContentResolver contentResolver = getContentResolver();
//Uri uri = Uri.parse("content://" + CalendarContract.AUTHORITY + "/calendars");
Uri uri = CalendarContract.Calendars.CONTENT_URI;
Cursor query = contentResolver.query(uri,null,null,null,null);
String[] columnNames = query.getColumnNames();
for(String columnName : columnNames) {
Log.d(TAG,"columnName -- > " + columnName);
}
query.close();
运行结果:
columnName -- > account_type
columnName -- > mutators
columnName -- > ownerAccount
columnName -- > allowedReminders
columnName -- > cal_sync3
columnName -- > cal_sync2
columnName -- > COALESCE(isPrimary, ownerAccount = account_name
columnName -- > maxReminders
columnName -- > cal_sync1
columnName -- > cal_sync10
columnName -- > account_name
columnName -- > cal_sync7
columnName -- > cal_sync6
columnName -- > canPartiallyUpdate
columnName -- > cal_sync5
columnName -- > sync_events
columnName -- > cal_sync4
columnName -- > canOrganizerRespond
columnName -- > calendar_color
columnName -- > cal_sync9
columnName -- > calendar_location
columnName -- > cal_sync8
columnName -- > dirty
columnName -- > visible
columnName -- > calendar_timezone
columnName -- > calendar_access_level
columnName -- > allowedAvailability
columnName -- > _sync_id
columnName -- > deleted
columnName -- > name
columnName -- > canModifyTimeZone
columnName -- > _id
columnName -- > calendar_color_index
columnName -- > allowedAttendeeTypes
columnName -- > calendar_displayName
所以,同学们可以通过这种方法研究都有哪些字段
插入事件
插入事件,当我们要做一件事情的时候,可以多问息为什么,怎么样这类问题。
到底怎么写呢?对吧!哪些字段是需要插入内容的呢?
不知道没关系呀,我们手动在手机上添加一条记录,读取出来,不就知道了吗?
插入记录:
测试代码
ContentResolver contentResolver = getContentResolver();
Uri uri = CalendarContract.Events.CONTENT_URI;
Cursor query = contentResolver.query(uri,null,null,null,null);
String[] columnNames = query.getColumnNames();
while(query.moveToNext()) {
Log.d(TAG,"=============================================");
for(String columnName : columnNames) {
Log.d(TAG,"field --- > " + columnName + "value -- > " + query.getString(query.getColumnIndex(columnName)));
}
Log.d(TAG,"=============================================");
}
query.close();
运行结果
=============================================
field --- > originalAllDayvalue -- > null
field --- > account_typevalue -- > LOCAL
field --- > exrulevalue -- > null
field --- > mutatorsvalue -- > com.android.calendar
field --- > originalInstanceTimevalue -- > null
field --- > allDayvalue -- > 1
field --- > allowedRemindersvalue -- > 0,1
field --- > rrulevalue -- > null
field --- > canOrganizerRespondvalue -- > 1
field --- > lastDatevalue -- > 1573084800000
field --- > visiblevalue -- > 1
field --- > calendar_idvalue -- > 1
field --- > hasExtendedPropertiesvalue -- > 0
field --- > calendar_access_levelvalue -- > 700
field --- > selfAttendeeStatusvalue -- > 0
field --- > allowedAvailabilityvalue -- > 0,1
field --- > eventColor_indexvalue -- > null
field --- > isOrganizervalue -- > 1
field --- > _sync_idvalue -- > null
field --- > calendar_color_indexvalue -- > null
field --- > _idvalue -- > 2
field --- > guestsCanInviteOthersvalue -- > 1
field --- > allowedAttendeeTypesvalue -- > 0,1,2
field --- > dtstartvalue -- > 1572998400000
field --- > guestsCanSeeGuestsvalue -- > 1
field --- > sync_data9value -- > null
field --- > sync_data8value -- > null
field --- > exdatevalue -- > null
field --- > sync_data7value -- > null
field --- > sync_data6value -- > null
field --- > sync_data1value -- > null
field --- > descriptionvalue -- > 记得带上人和设备
field --- > eventTimezonevalue -- > UTC
field --- > availabilityvalue -- > 0
field --- > titlevalue -- > 去红树林拍照
field --- > ownerAccountvalue -- > owner_account_local
field --- > sync_data5value -- > null
field --- > sync_data4value -- > null
field --- > sync_data3value -- > null
field --- > sync_data2value -- > null
field --- > durationvalue -- > null
field --- > lastSyncedvalue -- > 0
field --- > guestsCanModifyvalue -- > 0
field --- > cal_sync3value -- > null
field --- > rdatevalue -- > null
field --- > cal_sync2value -- > null
field --- > maxRemindersvalue -- > 5
field --- > cal_sync1value -- > null
field --- > cal_sync10value -- > null
field --- > account_namevalue -- > account_name_local
field --- > cal_sync7value -- > null
field --- > cal_sync6value -- > null
field --- > cal_sync5value -- > null
field --- > cal_sync4value -- > null
field --- > calendar_colorvalue -- > -30720
field --- > cal_sync9value -- > null
field --- > cal_sync8value -- > null
field --- > dirtyvalue -- > 1
field --- > calendar_timezonevalue -- > null
field --- > accessLevelvalue -- > 0
field --- > eventLocationvalue -- > 红树林深圳湾
field --- > hasAlarmvalue -- > 1
field --- > uid2445value -- > null
field --- > deletedvalue -- > 0
field --- > eventColorvalue -- > null
field --- > organizervalue -- > owner_account_local
field --- > eventStatusvalue -- > 1
field --- > customAppUrivalue -- > null
field --- > canModifyTimeZonevalue -- > 1
field --- > eventEndTimezonevalue -- > null
field --- > customAppPackagevalue -- > null
field --- > original_sync_idvalue -- > null
field --- > hasAttendeeDatavalue -- > 1
field --- > displayColorvalue -- > -30720
field --- > dtendvalue -- > 1573084800000
field --- > original_idvalue -- > null
field --- > sync_data10value -- > null
field --- > calendar_displayNamevalue -- > calendar_displayname_local
=============================================
所以,插入事件我们需要使用到哪些字段呢?
我们看这里:
规则:
- 您必须加入 CALENDAR_ID 和 DTSTART。
- 您必须加入 EVENT_TIMEZONE。如需获取系统中已安装时区 ID 的列表,请使用 getAvailableIDs()。请注意,如果您按使Intent 插入事件中所述通过 INSERT Intent 插入事件,则此规则不适用 — 在该情形下,系统会提供默认时区。
- 对于非重复事件,您必须加入 DTEND。
- 对于重复事件,您必须加入 DURATION,以及 RRULE 或 RDATE。请注意,如果您按使用 Intent 插入事件中所述通过 INSERT Intent 插入事件,则此规则不适用 — 在该情形下,您可以将 RRULE 与 DTSTART 和 DTEND 结合使用,日历应用会自动将其转换为持续时间。
向日历中插入事件,怎么写呢?
- 先通过前面的日历表,查询到日历的id,因为CALENDAR_ID是必须填写的
- 必须要填写DTSTART和EVENT_TIMEZONE
- 非重复事件,必须加入DTEND
- 如果重复事件,则要加入DURATION,RRULE 或 RDATE
//前面查询出来的
long calID = 1;
//时间创建
Calendar beginTime = Calendar.getInstance();
//Month value is 0-based. e.g., 0 for January.
//2019年,12月,7日,8点整
beginTime.set(2019,11,7,8,00);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
//2019年,12月,7日,8点,45分
endTime.set(2019,11,7,8,45);
long endMillis = endTime.getTimeInMillis();
Log.d(TAG,"beginTime -- > " + startMillis);
Log.d(TAG,"endTime -- > " + endMillis);
//准备好插入事件数据库的内容
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
//开始时间
values.put(CalendarContract.Events.DTSTART,startMillis);
//结束时间
values.put(CalendarContract.Events.DTEND,endMillis);
//标题
values.put(CalendarContract.Events.TITLE,"去深圳备战坐高铁");
//描述
values.put(CalendarContract.Events.DESCRIPTION,"早上八点钟的高铁去陕西");
//日历ID
values.put(CalendarContract.Events.CALENDAR_ID,calID);
//时间时区
String timeZone = TimeZone.getDefault().getID();
Log.d(TAG,"time zone -- > " + timeZone);
values.put(CalendarContract.Events.EVENT_TIMEZONE,timeZone);
Uri uri = cr.insert(CalendarContract.Events.CONTENT_URI,values);
Log.d(TAG,"insert result --- > " + uri);
运行结果:
事件的更新和删除就不用说了吧,跟普通数据库操作一样。
插入提醒
接下来我们插入提醒内容
以下是提醒表的字段:
规则:插入新提醒时,您必须加入以上所有字段
//前面我们深圳备战的id,可过查询得到
long eventID = 3;
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
//15分钟前进行提醒
values.put(CalendarContract.Reminders.MINUTES,15);
values.put(CalendarContract.Reminders.EVENT_ID,eventID);
values.put(CalendarContract.Reminders.METHOD,CalendarContract.Reminders.METHOD_ALERT);
Uri uri = cr.insert(CalendarContract.Reminders.CONTENT_URI,values);
Log.d(TAG,"result uri -- > " + uri);
执行结果:
删除和修改这里就不说了,跟普通数据库操作一样。
其他功能(附录)
这是官方文档的目录
官方文档地址:
学习中遇到问题就发帖子提问吧!