Android官方文档之Calendar Provider
Posted vanpersie_9987
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android官方文档之Calendar Provider相关的知识,希望对你有一定的参考价值。
Calendar Provider是一个用于提供用户标记在日历上事件的数据仓库。Calendar Provider 的API提供了包括增删改查在内的一系列操作日历事件的方法。
您可以在自己的应用程序或Adapter中使用Calendar Provider 的API。Adapter的相关用法将在本文的最后介绍。
通常来说,要访问日历应用程序中的数据,需要您在manifest中声明权限;为了简化操作, Calendar Provider 提供了一系列的Intents,使用这些Intents可以方便添加、查看、编辑 日历应用程序中的事件(take users to the Calendar application to insert, view, and edit events),这样一来,您可以与日历应用程序交互以获取日历应用的数据信息,而且不需要您声明权限,也不需要自己建立日历事件的视图。
本文将介绍Calendar Provider的相关内容,如需访问官方原文,您可以点击这个链接:《Calendar Provider》。
基础概要(Basics)
Content providers存储了数据,并提供了API以便应用程序可以访问Content providers管理的数据。Content providers管理的数据通常是关系型数据库中的一系列表结构,表中的每一行表示一条记录(record),每一列表示该记录的一个特定属性。通过Calendar Provider API,您可以方便地读写日历应用程序中由Calendar Provider管理的一系列表中的记录。
每一个content provider都向外界暴露了一个独一无二的Uri
地址,通过这个地址,您可以访问到这个content provider,以读写它所管辖的数据。所有的Uri都以content://
开头。为了能访问到Calendar Provider中表的数据,应当采用 <class>.CONTENT_URI
的Uri格式,其中class就表示表名,如Events.CONTENT_URI
。
下图展示了Calendar Provider管辖的数据模型,该模型由一个叫做Events
的表作为主表,通过该表可以链接至其他表:
CalendarContract
类中定义了Calendar Provider中各种表所对应的模型,并存储相关信息,如下表所示:
表名(模型名) Tables(class) | 描述(Description) |
---|---|
CalendarContract.Calendars | 该表中包含了具体的日历信息(calendar-specific information),每一行表示一个日期中的具体内容,如名字,颜色,同步信息 等 |
CalendarContract.Events | 该表中包含了具体的事件信息(event-specific information),每一行表示一个独立的事件,如,事件标题、发生地点、起始时间、结束时间 等(event title, location, start time, end time, and so on)。可以将事件设置为一次性事件或重复事件,而参加者(Attendees)、提醒信息(reminders)、其他属性信息(extended properties)都被存储于相应的表中,这些表中都各自包含一个EVENT_ID 字段,作为Events 表的外键(一对多:一个事件可以有多个提醒信息 等。但一个提醒信息只能针对一个事件) |
CalendarContract.Instances | 该表记录了事件的起始和结束时间。每一行表示一个事件的发生情况。对于一次性事件,该表和Events表是一对一关系(一对一:一个事件只有一对起讫时间,而一对起讫时间也只对应一个事件) ;对于重复事件,该表和Events表是多对多关系(多对多:一个事件可以有多对起讫时间,而一对起讫时间也可以应用于多个事件) |
CalendarContract.Attendees | 该表记录了事件的参加者信息(holds the event attendee (guest) information),每一行表示了一个事件中的一个参加者信息。 |
CalendarContract.Reminders | 该表记录了警告和通知信息(alert/notification data),每一行表示一个事件的警告,一个事件可以有多个警告,但有最大警告数的限制,用MAX_REMINDERS 指定。提醒信息会在设定提醒时间之前的若干分钟进行预提醒,以通知用户做好准备 |
使用Calendar Provider API需要注意的事:
插入、更新、查看日历事件(Inserting, updating, and viewing calendar events):为了读写Calendar Provider中的数据,需要您添加读写权限。若您并不打算开发一款功能齐全的日历应用程序或者同步adapter,那么您无需申请权限(, if you’re not building a full-fledged calendar application or sync adapter, requesting these permissions isn’t necessary),您可以使用Intents访问系统自带的日历应用程序,并返回结果。有关Calendar Intents的内容,我将在后续翻译。
同步adapter(Sync adapters):同步adapter可以从用户的设备中同步日历数据(A sync adapter synchronizes the calendar data on a user’s device with another server or data source)。
CalendarContract.Calendars
和CalendarContract.Events
表中都提供了同步adapter的字段。Calendar Provider和其他应用程序不可修改这个字段中的内容。事实上,这对于外部应用是隐藏的。
用户权限(User Permissions)
为了查询Calendar Provider中的数据,您应在manifest文件中添加READ_CALENDAR
权限,为了增、删、改 Calendar Provider中的数据,您应在manifest文件中添加WRITE_CALENDAR
权限,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
<uses-sdk android:minSdkVersion="14" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
...
</manifest>
日历表(Calendars Table)
CalendarContract.Calendars
表中包含了日历的详细信息。下面介绍了表中的字段信息。
字段(Constant) | 描述(Description) |
---|---|
NAME | 日历的名字 |
CALENDAR_DISPLAY_NAME | 向用户展示的日历名字(The name of this calendar that is displayed to the user) |
VISIBLE | boolean类型,用于决定是否将日历展示给用户。若值为0表示与该日历绑定的事件不予显示。值为1则相反。该值直接影响了CalendarContract.Instances 表中的内容 |
SYNC_EVENTS | boolean类型,表示是否同步设备中的日历数据。 |
查询日历表(Querying a calendar)
下面演示了查询的示例,需要注意的是,您需要在异步线程中执行查询操作:
// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME, // 2
Calendars.OWNER_ACCOUNT // 3
};
// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
// Run query
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
+ Calendars.ACCOUNT_TYPE + " = ?) AND ("
+ Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
"sampleuser@gmail.com"};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
// Use the cursor to step through the returned records
while (cur.moveToNext()) {
long calID = 0;
String displayName = null;
String accountName = null;
String ownerName = null;
// Get the field values
calID = cur.getLong(PROJECTION_ID_INDEX);
displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
// Do something with the values...
...
}
修改日历表(Modifying a calendar)
修改日历表实际上是修改表中的某一行或若干行数据,您可以直接在Uri中追加需要修改的行(调用withAppendedId()
),或使用selection 筛选行,selection 应为_id=?
形式,而selectionArg 是calendar表的_ID
,示例如下:
private static final String DEBUG_TAG = "MyActivity";
...
long calID = 2;
ContentValues values = new ContentValues();
// The new display name for the calendar
values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);
事件表(Events Table)
CalendarContract.Events
表记录了事件的具体信息,为了操作该表,您同样需要添加相应权限。
下面是Events 表中的字段信息:
字段(Constant) | 描述(Description) |
---|---|
CALENDAR_ID | Calender表的外键 |
ORGANIZER | 事件的Email |
TITLE | 事件的标题 |
EVENT_LOCATION | 事件的发生地点 |
DESCRIPTION | 事件描述 |
DTSTART | 事件起始时间 |
DTEND | 事件结束时间 |
EVENT_TIMEZONE | 事件发生的时区 |
EVENT_END_TIMEZONE | 结束时间的时区 |
DURATION | 事件持续时间,使用RFC5545 格式,如PT1H 表示事件将持续一小时,P2W 表示事件将持续两周 |
ALL_DAY | boolean类型,表示是否为全天候事件,0表示“是全天候事件”,1表示“不是全天候事件” |
RRULE | 循环触发事件的条件,如:FREQ=WEEKLY;COUNT=10;WKST=SU ,您可以参考这个例子 |
RDATE | 事件循环的时间,您可以与RRULE一并使用 |
AVAILABILITY | 将此事件视为忙碌时间还是可调度的空闲时间 |
GUESTS_CAN_MODIFY | 来宾是否可修改事件 |
GUESTS_CAN_INVITE_OTHERS | 是否可以邀请其他来宾 |
GUESTS_CAN_SEE_GUESTS | 来宾是否可查看参加者列表 |
添加事件(Adding Events)
当您打算插入一条新的事件时,推荐使用INSERT Intent,当然您也可以直接插入事件,需要注意的事项如下:
必须包含
CALENDAR_ID
和DTSTART
字段;必须包含
EVENT_TIMEZONE
字段,调用getAvailableIDs()
方法获取时区的ID。如使用Intent插入事件,则系统会自动包含时区信息;若是一次性事件,则必须包含
DTEND
字段;对于一次性事件,应包含
DURATION
字段,而不应包含RRULE 或 RDATE
字段,如使用Intent插入事件,则系统会自动包含事件的持续时间;
下面通过一个例子演示插入事件,您同样需要把操作放在异步线程中执行:
long calID = 3;
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 9, 14, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 9, 14, 8, 45);
endMillis = endTime.getTimeInMillis();
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, "Jazzercise");
values.put(Events.DESCRIPTION, "Group workout");
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
Uri uri = cr.insert(Events.CONTENT_URI, values);
// get the event ID that is the last element in the Uri
long eventID = Long.parseLong(uri.getLastPathSegment());
//
// ... do something with event ID
//
//
您可以通过返回的事件ID执行其他日历操作,如在实践中加入参加者或提醒(to add attendees or reminders to an event)。
修改事件(Updating Events)
推荐使用EDIT Intent
修改事件,但也可以手动修改,示例如下:
private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// The new title for the event
values.put(Events.TITLE, "Kickboxing");
updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);
删除事件(Deleting Events)
示例如下:
private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 201;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri deleteUri = null;
deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().delete(deleteUri, null, null);
Log.i(DEBUG_TAG, "Rows deleted: " + rows);
参加人员表(Attendees Table)
CalendarContract.Attendees
表的一行表示一个事件的参加者信息。调用query()
方法将返回某个事件(表中的EVENT_ID
字段为Event表的外键,该字段的值决定了参加者所属的事件)的参加者列表。
下面介绍了参加者表的字段信息:
字段(Constant) | 描述(Description) |
---|---|
EVENT_ID | Event表的外键 |
ATTENDEE_NAME | 参加者的姓名 |
ATTENDEE_EMAIL | 参加者的Email |
ATTENDEE_RELATIONSHIP | 参加者的职责,下列值之一:RELATIONSHIP_ATTENDEE 、RELATIONSHIP_NONE 、RELATIONSHIP_ORGANIZER 、RELATIONSHIP_PERFORMER 、RELATIONSHIP_SPEAKER |
ATTENDEE_TYPE | 参加者的重要程度,下列值之一:TYPE_REQUIRED 、TYPE_OPTIONAL |
ATTENDEE_STATUS | 参加者的状态,下列值之一:ATTENDEE_STATUS_ACCEPTED 、ATTENDEE_STATUS_DECLINED 、ATTENDEE_STATUS_INVITED 、ATTENDEE_STATUS_NONE 、ATTENDEE_STATUS_TENTATIVE |
添加参加者(Adding Attendees)
下面是一个添加参加者的示例,需要注意的是必须包含EVENT_ID
字段:
long eventID = 202;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Attendees.ATTENDEE_NAME, "Trevor");
values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);
提醒事项表(Reminders Table)
CalendarContract.Reminders
表中的一行表示了某个事件的提醒事项,下面介绍该表的字段信息:
字段(Constant) | 描述(Description) |
---|---|
EVENT_ID | Event表的外键 |
MINUTES | 事件发生前的分钟数,应在达到该时间时发出提醒 |
METHOD | 服务器上设置的提醒方法。下列值之一:METHOD_ALERT 、METHOD_DEFAULT 、METHOD_EMAIL 、METHOD_SMS |
添加提醒事项(Adding Reminders)
long eventID = 221;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Reminders.MINUTES, 15);
values.put(Reminders.EVENT_ID, eventID);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
Uri uri = cr.insert(Reminders.CONTENT_URI, values);
实例表(Instances Table)
CalendarContract.Instances
表中的每一行记录了一个事件的起讫时间,该表中的内容不可修改,只能查询:
字段(Constant) | 描述(Description) |
---|---|
BEGIN | 实例的开始时间,以协调世界时毫秒数表示 |
END | 实例的结束时间,以协调世界时毫秒数表示 |
END_DAY | 与日历时区相应的实例儒略历结束日(The Julian end day of the instance, relative to the Calendar’s time zone) |
END_MINUTE | 从日历时区午夜开始计算的实例结束时间(分钟) |
EVENT_ID | Event表的主键 |
START_DAY | 与日历时区相应的实例儒略历开始日 |
START_MINUTE | 从日历时区午夜开始计算的实例开始时间(分钟) |
查询实例表(Querying the Instances table)
private static final String DEBUG_TAG = "MyActivity";
public static final String[] INSTANCE_PROJECTION = new String[] {
Instances.EVENT_ID, // 0
Instances.BEGIN, // 1
Instances.TITLE // 2
};
// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
...
// Specify the date range you want to search for recurring
// event instances
Calendar beginTime = Calendar.getInstance();
beginTime.set(2011, 9, 23, 8, 0);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2011, 10, 24, 8, 0);
long endMillis = endTime.getTimeInMillis();
Cursor cur = null;
ContentResolver cr = getContentResolver();
// The ID of the recurring event whose instances you are searching
// for in the Instances table
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"};
// Construct the query with the desired date range.
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);
// Submit the query
cur = cr.query(builder.build(),
INSTANCE_PROJECTION,
selection,
selectionArgs,
null);
while (cur.moveToNext()) {
String title = null;
long eventID = 0;
long beginVal = 0;
// Get the field values
eventID = cur.getLong(PROJECTION_ID_INDEX);
beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
title = cur.getString(PROJECTION_TITLE_INDEX);
// Do something with the values.
Log.i(DEBUG_TAG, "Event: " + title);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(beginVal);
DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));
}
}
日历Intent(Calendar Intents)
使用Calendar Intents,您无需添加权限,就能访问Calendar Provider管理的表数据。
下面介绍了由Calendar Provider支持的Intent:
Action | URI | Description | Extras |
---|---|---|---|
VIEW | content://com.android.calendar/time/<ms_since_epoch> | 打开日历后定位到 <ms_since_epoch> 指定的时间 | 无 |
VIEW | content://com.android.calendar/events/<event_id> | 查看 <event_id> 指定的事件 | CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
EDIT | content://com.android.calendar/events/<event_id> | 编辑 <event_id> 指定的事件 | CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
EDIT INSERT | content://com.android.calendar/events | 创建事件 | 下表列出的任一 Extra |
Intent Extra | Description |
---|---|
Events.TITLE | 事件的名字 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME | 事件开始时间,以从公元纪年开始计算的毫秒数表示 |
CalendarContract.EXTRA_EVENT_END_TIME | 事件结束时间,以从公元纪年开始计算的毫秒数表示 |
CalendarContract.EXTRA_EVENT_ALL_DAY | boolean类型,0表示全天候事件,1表示非全天候事件 |
Events.EVENT_LOCATION | 事件的地点 |
Events.DESCRIPTION | 事件描述 |
Intent.EXTRA_EMAIL | 受邀者Email地址列表,以逗号分隔 |
Events.RRULE | 事件的重复发生规则 |
Events.ACCESS_LEVEL | 事件是私人性质还是公共性质 |
Events.AVAILABILITY | 将此事件视为忙碌时间还是可调度的空闲时间 |
使用Intent添加事件(Using an intent to insert an event)
当您在自己的应用中使用Intent启动日历应用程序时,应用会转到日历来完成事件添加操作,可以将预填充日历事件的详细信息附加在INSERT
Intent中的extra 字段里,待程序切换至日历应用后,这些信息会自动填充至相应的栏目中,用户只需选择保存、编辑、取消 等选项即可。
下面通过一段代码演示了添加一条发生于2012年1月19日的待办事件,该事件的起始时间为上午7:30 ,结束时间为上午8:30:
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 0, 19, 8, 30);
Intent intent = new Intent(Intent.ACTION_INSERT)
.setData(Events.CONTENT_URI)
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
.putExtra(Events.TITLE, "Yoga")
.putExtra(Events.DESCRIPTION, "Group class")
.putExtra(Events.EVENT_LOCATION, "The gym")
.putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
.putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
startActivity(intent);
使用Intent编辑事件(Using an intent to edit an event)
long eventID = 208;
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT)
.setData(uri)
.putExtra(Events.TITLE, "My New Title");
startActivity(intent);
使用Intent查看日历信息(Using intents to view calendar data)
查看日历信息,有两种查看结果:
查询指定日期的日历信息;
查询指定日期的事件。
查看指定日期的日历信息如下:
// A date-time specified in milliseconds since the epoch.
long startMillis;
...
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
ContentUris.appendId(builder, startMillis);
Intent intent = new Intent(Intent.ACTION_VIEW)
.setData(builder.build());
startActivity(intent);
查看具体事件如下:
long eventID = 208;
...
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_VIEW)
.setData(uri);
startActivity(intent);
同步Adapters(Sync Adapters)
使用同步Adapters访问Calendar Provider中数据的方式 与上述方式差别不大:
同步Adapter需要通过将
CALLER_IS_SYNCADAPTER
设置为true
来表明它是同步适配器;需提供
ACCOUNT_NAME
和ACCOUNT_TYPE
作为URI
中的查询参数;与应用或小工具相比,同步适配器拥有写入权限的列更多。 例如,应用只能修改日历的少数几种特性, 例如其名称、显示名称、能见度设置以及是否同步日历。 相比之下,同步适配器不仅可以访问这些列,还能访问许多其他列, 例如日历颜色、时区、访问级别、地点等等。不过,同步适配器受限于它指定的
ACCOUNT_NAME
和ACCOUNT_TYPE
。
同步Adapter的示例如下:
static Uri asSyncAdapter(Uri uri, String account, String accountType) {
return uri.buildUpon()
.appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
.appendQueryParameter(Calendars.ACCOUNT_NAME, account)
.appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
}
以上是关于Android官方文档之Calendar Provider的主要内容,如果未能解决你的问题,请参考以下文章