英文原文:http://developer.android.com/guide/topics/providers/calendar-provider.html

Calendar Provider 是用来存放用户日历事件(event)的数据库。 通过 Calendar Provider 的 API ,可以完成对 calendars、events、attendees、reminders 表的查询、插入、修改和删除等操作。

应用程序和 Sync Adapter 都可以使用 Calender Provider API。 对于这两种不同类型的程序,调用的规则也不一样。 本文的重点是介绍应用程序是如何使用 Calender Provider API 的。 Sync Adapter 的使用方式会有所不同,请参阅Sync Adapters

通常,如果要读写日历数据,应用程序必须在 Manifest 文件中声明相应的权限,这在用户权限一节中将会介绍。 为了便于完成一些常见的操作, Calender Provider 提供了一些 Intent,这将在Calendar Intent一节中介绍。 这些 Intent 可以让用户打开 Calendar 应用,并完成插入、查看、编辑事件的操作。 用户在 Calendar 应用中完成交互后,将返回初始的应用中。 这样发起方应用就不必申请相应权限,也不需要提供浏览和创建事件的用户界面了。

基础知识

Content Provider 负责存放数据并提供数据访问方式。 Android 系统内置的 Content Provider (包括 Calendar Provider)一般是以关系型数据库表的形式提供数据的, 行数据代表一条记录,列数据表示字段类型和意义。 应用程序和 Sync Adapter 通过 Calendar Provider API 来访问数据库表中的用户日历数据。

每个 Content Provider 都会公开一个公共的 URI (封装为一个 Uri 对象),它唯一标识了某部分数据。 如果 Content Provider 管理着多组数据(多张数据表),则各组数据都会有单独的 URI。 所有用于 Provider 的 URI 都以字符串 "content://" 开头,表示这些数据是由某个 Content Provider 进行管理的。 Calendar Provider 为其内部类(表)的 URI 定义了很多常量。这些 URI 的格式均为 <class>.CONTENT_URI。比如 Events.CONTENT_URI

图 1 为 Calendar Provider 数据模型的示意图,给出了主表及与其他表的关联字段。

图 1. Calendar Provider 数据模型

一个用户可以拥有多个 Calendar,每个 Calendar 可以与不同类型的帐号关联(Google Calendar、Exchange 等)。

CalendarContract 定义了 Calendar 和 Event 的数据模型。这些数据存放在以下数据表中。

数据表(类) 说明

CalendarContract.Calendars

该表存放日程的定义数据。每行表示一条日程的详细信息,如名称、颜色、同步信息等。
CalendarContract.Events 该表存放事件的定义数据。每行表示一个事件,内容包括 — 事件标题、位置、起始时间、结束时间等等。 事件可以是一次性的,也可以重复多次触发。 参与人员、提醒闹钟及附加属性都存放在其他表中,并通过 EVENT_ID 字段与 Events 表中的 _ID 关联。
CalendarContract.Instances 该表存放事件每次触发时的起始时间和结束时间。一次性事件只会1:1对应一条实例记录。 对于重复触发的事件而言,则会自动生成多条实例记录,对应每一次的触发。
CalendarContract.Attendees 该表存放事件的参与人员(来宾)信息。每行代表一位人员。 内容包括人员类型和与会反馈。
CalendarContract.Reminders 该表存放闹钟/通知数据。每行代表一次闹钟提醒。 一个事件可以拥有多个闹钟提醒。每个事件可拥有的最大提醒数在 MAX_REMINDERS 中定义,这是由拥有该日程的 sync adapter 设置的。 提醒定义了事件触发前的分钟数,以及提醒用户的方式。

Calendar Provider API 的设计初衷,是既要灵活又要功能强大。另一方面,良好的用户体验、保证日程数据的安全也非常重要。 为此,在使用这些 API 时,必须注意以下几点:

  • 插入、更新和查询日程事件。 如果要直接插入、修改和查询 Calendar Provider 中的事件数据,需要获得合适的权限。 不过,如果还未建立完整的日历应用或 Sync Adapter,就没必要申请这些权限。 这时就可以通过 Intent,把读写操作交给 Android 内置 Calendar 应用去完成。 在使用这些 Intent 时,用户会被带入 Calendar 应用,在预置的表单中进行操作。待操作完毕后,再返回调用方应用。 通过这种调用内置 Calendar 来完成常用操作的方式,可以向用户提供一种风格统一、容错性较强的界面。 这也是推荐的访问方式。详情请参阅Calendar Intent
  • Sync Adapter。 Sync Adapter 负责将用户设备上的日程数据与服务器或数据源保持同步。在 CalendarContract.Calendars和 CalendarContract.Events 表中,有些字段是留给 Sync Adapter 使用的。 Provider 和应用程序都不要去修改这些字段中的数据。 实际上只有以 Sync Adapter 的方式去访问时,这些字段才是可见的。 关于 Sync Adapter 的详细信息,请参阅 Sync Adapter

用户权限

如果要读取日程数据,应用程序必须在 Manifest 文件中包含 READ_CALENDAR 权限。如果是删除、插入或修改日程数据,则必须包含 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 表

CalendarContract.Calendars 表存放着每项日程的详细信息。 以下字段均可由应用程序和 Sync Adapter 写入。 完整的字段清单请参阅手册 CalendarContract.Calendars

常量名称 说明
NAME 日程的名称。
CALENDAR_DISPLAY_NAME 向用户显示的日程名称。
VISIBLE 布尔值,标明该日程是否可见。 为 0 表示不显示与该日程关联的事件,为 1 则表示需要显示。 该值将会影响 CalendarContract.Instances 表中生成的记录。
SYNC_EVENTS 布尔值,标明该日程是否需要同步,及事件是否需要本地保存。 为 0 表示不需要同步或者不保存。为 1 则表示应该同步并在设备中保存事件。

查询日程

以下例子演示了如何读取某个用户的日程信息。 为了简化起见,查询操作是在用户界面线程(“主线程”)中进行的。 在实际应用中,这步操作不应放在主线程中,而应该在异步线程中完成。更多信息,请参阅Loaders。 如果不仅要读取数据,还要修改的话,请参阅 AsyncQueryHandler

// 映射数组。为数组建立索引,就不需要动态检索,以便提高性能。
public static final String[] EVENT_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME, // 2
Calendars.OWNER_ACCOUNT // 3
}; // 映射数组的索引
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;

为什么查询条件中必须包含 ACCOUNT_TYPE字段 ?

如果要查询 Calendars.ACCOUNT_NAME 字段,就必须在查询语句中包含 Calendars.ACCOUNT_TYPE 字段。这是因为 ACCOUNT_NAME 及其 ACCOUNT_TYPE 合在一起才能唯一确定一个账户。 ACCOUNT_TYPE 是与认证方式(authenticator)对应的一个字符串,在使用 AccountManager注册账户时需要用到这个认证方式。 对于那些和账户无关的日程,还有一种特殊的账户类型,叫做 ACCOUNT_TYPE_LOCAL。 ACCOUNT_TYPE_LOCAL 类型的账户不会进行同步。

接下来就是构建查询,在查询语句中指定查询条件。 这里要查询的日程,ACCOUNT_NAME为“sampleuser@google.com”, ACCOUNT_TYPE为“com.google”,OWNER_ACCOUNT为“sampleuser@google.com”。 如果要查询某用户可查看的所有日程,而不仅限于属于用户自己的日程,请去掉OWNER_ACCOUNT条件。 查询将会返回一个 Cursor 对象,通过该游标可以遍历返回的结果数据集。 关于 Content Provider 查询的更多介绍,请参阅 Content Provider

// 执行查询
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"};
// 提交查询并获取结果 Cursor 对象。
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);

下面通过游标遍历查询结果。 这里用到了一开始定义的常量,返回各个字段的数据。

// 利用游标遍历结果记录集
while (cur.moveToNext()) {
long calID = 0;
String displayName = null;
String accountName = null;
String ownerName = null; // 读取各个字段的数据
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); // 用这些数据进行一些操作... ...
}

修改日程

如果要修改日程数据,可以把该项日程的 _ID 作为 URI (withAppendedId()) 的附带 ID 参数,或者作为第一个查询条件。 作为查询条件时,应该以"_id=?"开头,第一个参数selectionArg应该是该项日程的 _ID。 还可以把 ID 加入 URI 编码中执行数据更新操作。 以下给出了通过 URI 方式 (withAppendedId()) 修改日程显示名称的例子:

private static final String DEBUG_TAG = "MyActivity";
...
long calID = 2;
ContentValues values = new ContentValues();
// 该日程的新名称
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);

插入日程

日程本来就设计为主要由 Sync Adapter 来维护的,因此只能由一个 Sync Adapter 来插入一项新的日程。 多数情况下,应用程序只能对日程做一些外观上的修改,比如修改显示名称。 如果应用程序需要新建一项日程,可以作为 Sync Adapter 来执行插入操作, ACCOUNT_TYPE 设为 ACCOUNT_TYPE_LOCAL。 ACCOUNT_TYPE_LOCAL 是一种专为日程设计的特殊账户,它不与实际的本地账户关联。 这种日程数据也不会与服务器同步。 关于 Sync Adapter 的介绍,请参阅 Sync Adapter

Events 表

CalendarContract.Events 表存放着每个事件的详细信息。 如果要新增、修改或删除事件,应用程序必须在 Manifest 文件 中包含 WRITE_CALENDAR 权限。

以下字段均可由应用程序和 Sync Adapter 写入。 完整的字段清单,请参阅手册 CalendarContract.Events

常量 说明
CALENDAR_ID 事件所属日程的 _ID
ORGANIZER 日程编制者(所有者)的 Email。
TITLE 事件标题。
EVENT_LOCATION 事件所在地。
DESCRIPTION 事件说明。
DTSTART 事件起始 UTC 时间,单位为自1970年1月1日以来的毫秒数。
DTEND 事件结束 UTC 时间,单位为自1970年1月1日以来的毫秒数。
EVENT_TIMEZONE 事件时区。
EVENT_END_TIMEZONE 事件结束时间的时区。
DURATION 事件的持续时间,格式为RFC5545。 比如,"PT1H"表示事件会持续 1 小时,"P2W"则表示持续 2 周。
ALL_DAY 为 1 表示该事件会占用全天时间,类似于时区设置中的定义。 为 0 表示其为常规事件,可以在一天中的任意时刻开始和结束。
RRULE 事件重复规则。比如:"FREQ=WEEKLY;COUNT=10;WKST=SU"。 更多示例请查看 RFC5545
RDATE 事件重复的日期。通常是把 RDATE 与 RRULE 结合起来定义一组重复规则。详情请参阅 RFC5545 说明
AVAILABILITY 标明该事件是在空闲时参与调度,还是在忙时参与。
GUESTS_CAN_MODIFY 来宾是否可以修改事件。
GUESTS_CAN_INVITE_OTHERS 来宾是否可以邀请其他人参加。
GUESTS_CAN_SEE_GUESTS 来宾是否能看到参加人员名单。

添加事件

推荐使用 INSERT 来插入一条新的事件,这在 利用 Intent 插入事件 一节中将会介绍。 不过在必要时,也可以直接插入一条事件记录。本节将介绍这种方式。

下面列出了插入新事件需要遵守的规则:

下面给出一个插入事件的例子。简化起见,此例运行于 UI 线程中。 在实际应用中,插入和修改操作都应该在后台的异步线程中完成。详情请参阅 AsyncQueryHandler

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); // 读取事件 ID,也就是 Uri 的最后一部分
long eventID = Long.parseLong(uri.getLastPathSegment());
//
// ... 利用 ID 完成一些处理
//
//

注意: 上述例子中,读取已创建事件 ID 的方式是一种最简单的方法。 在实际应用中,往往需要利用事件 ID 来对日程进行某些操作 — 比如:添加参与人员、增加事件提醒。

修改事件

如果需要让用户编辑事件,建议使用 EDIT Intent,这在 利用 Intent 编辑事件 一节中将会介绍。 不过在必要时,也可以直接编辑事件。 在修改事件时,给出事件 _ID 的方式可以是附在 Uri 后面( withAppendedId() ),也可以是作为查询条件的第一个参数。 查询条件应该以 "_id=?" 开头,第一个 selectionArg 应该是事件的 _ID 。 也可以使用不带 ID 的查询语句来更新数据。 下面给出一个更新事件数据的例子,这里将用 withAppendedId() 的方式修改事件的标题:

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// 新的标题
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);

删除事件

可以将 _ID 附在 URI 后面进行删除操作,也可以用标准的查询语句来完成。 如果采用前一种方式,就不能再用查询语句的方式。 删除操作有两种版本:作为应用程序、作为 Sync Adapter。 应用程序版本的删除就是把 deleted 字段置为 1。这会告诉 Sync Adapter 此条记录已被删除,同时服务器上也应该完成删除操作。 Sync Adapter 版本的删除则会在数据库中删除该条事件及所有相关数据。 以下例子演示了应用程序版本的删除,用到了 _ID :

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 表

CalendarContract.Attendees 表的每行记录代表参加事件的一位人员(来宾)。以 EVENT_ID 为参数调用 query() ,将会返回该事件的参加人员清单。这里的 EVENT_ID 必须与事件的实际 _ID一致。

下表列出了 Attendees 表中可供写入的字段。在插入新的人员记录时,必须包含除ATTENDEE_NAME外的所有这些字段。

常量名称 说明
EVENT_ID 事件 ID。
ATTENDEE_NAME 参加人员的姓名。
ATTENDEE_EMAIL 参加人员的 Email 地址。
ATTENDEE_RELATIONSHIP

该人员与事件的关系。可为下列值之一:

ATTENDEE_TYPE

参加人员的类型。可为下列值之一:

ATTENDEE_STATUS

参加人员的出席状况。可为下列值之一:

添加参加人员

以下给出了添加一个事件参与人员的例子。 请注意必须要给出 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 表

CalendarContract.Reminders 表的每行记录代表针对某事件的一条系统提醒。以 EVENT_ID 为参数调用 query() 时,将会返回相关系统提醒的清单。

下表列出了 Reminders 表中可供写入的字段。 在插入一条新的提醒数据时,必须包含所有这些字段。 请注意,在 CalendarContract.Calendars 表中,定义了 Sync Adapter 支持的提醒类型。 详情请参阅 ALLOWED_REMINDERS

常量名称 说明
EVENT_ID 事件 ID。
MINUTES 在事件发生之前多少分钟进行提醒。
METHOD

提醒方式,这是服务器上的设置。可为下列值之一:

添加提醒

下属例子为某个事件添加了一条提醒。这条提醒将会在事件发生前 15 分钟触发。

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 表

CalendarContract.Instances 表存放着事件的起始和结束时间。 每行记录代表事件的一次实例。 Instances 是不可写的,仅用于查询事件的发生经历。

下表列出了 Instances 表中可供查询的部分字段。 请注意时区是由 KEY_TIMEZONE_TYPE 和 KEY_TIMEZONE_INSTANCES 定义的。

常量 说明
BEGIN 事件该次实例的起始时间,单位为 UTC 毫秒数。
END 事件该次实例的结束时间,单位为 UTC 毫秒数。
END_DAY 事件该次实例的结束日期,Julian 历法,并与 Calendar 当前时区相关。
END_MINUTE 事件该次实例的结束时间,单位是自 Calendar 当前时区 0 点开始的分钟数。
EVENT_ID 该次实例的事件 _ID 。
START_DAY 事件该次实例的开始日期,Julian 历法,并与 Calendar 当前时区相关。
START_MINUTE 事件该次实例的开始时间,单位是自 Calendar 当前时区 0 点开始的分钟数。

查询 Instances 表

如果要查询 Instances 表,需要在查询 URI 中指定一个时间范围。 在以下例子中,通过实现 CalendarContract.EventsColumns 接口, CalendarContract.Instances 读取了 TITLE 字段。也就是说,通过数据库映射层返回了 TITLE ,而不是通过查询底层数据表 CalendarContract.Instances

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
}; // 为上面的映射数组定义索引常量
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
... // 定义要查询的事件实例的日期范围
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(); // 要在 Instances 表中查询的事件 ID
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"}; // 根据日期范围构造查询
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis); // 提交查询
cur = cr.query(builder.build(),
INSTANCE_PROJECTION,
selection,
selectionArgs,
null); while (cur.moveToNext()) {
String title = null;
long eventID = 0;
long beginVal = 0; // 读取各字段的值
eventID = cur.getLong(PROJECTION_ID_INDEX);
beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
title = cur.getString(PROJECTION_TITLE_INDEX); // 利用这些数据完成一些操作
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

读写日程数据时,应用程序并不需要申请权限。 也可以利用 Android 内置 Calendar 应用的 Intent 来完成读写操作。 下表列出了 Calendar Provider 支持的 Intent:

Action URI 说明 附加数据
VIEW

content://com.android.calendar/time/<ms_since_epoch>

也可以用 CalendarContract.CONTENT_URI 来引用该 URI。关于该 Intent 的使用实例,请参阅 使用 Intent 查看日程数据

打开日历,时间由<ms_since_epoch>指定。
VIEW

content://com.android.calendar/events/<event_id>

也可以用 Events.CONTENT_URI 来引用该 URI。关于该 Intent 的使用实例,请参阅 使用 Intent 查看日程数据

查看 <event_id> 指定的事件。 CalendarContract.EXTRA_EVENT_BEGIN_TIMECalendarContract.EXTRA_EVENT_END_TIME
EDIT content://com.android.calendar/events/<event_id> 也可以用 Events.CONTENT_URI. 来引用该 URI。关于该 Intent 的使用实例,请参阅 使用 Intent 编辑日程数据 编辑 <event_id> 指定的事件。 CalendarContract.EXTRA_EVENT_BEGIN_TIMECalendarContract.EXTRA_EVENT_END_TIME
EDITINSERT

content://com.android.calendar/events

也可以用 Events.CONTENT_URI 来引用该 URI。关于该 Intent 的使用实例,请参阅 使用 Intent 插入日程数据.

创建事件。 本表后面列出的任何附加数据。

下表列出了 Calendar Provider 支持的 Intent 附加数据:

Intent Extra 说明
Events.TITLE 事件名称。
CalendarContract.EXTRA_EVENT_BEGIN_TIME 事件的起始时间,单位是1970年1月1日以来的毫秒数。
CalendarContract.EXTRA_EVENT_END_TIME 事件的结束时间,单位是1970年1月1日以来的毫秒数。
CalendarContract.EXTRA_EVENT_ALL_DAY 布尔值,标明事件是否占用一整天。值为truefalse
Events.EVENT_LOCATION 事件所在地区。
Events.DESCRIPTION 事件描述信息。
Intent.EXTRA_EMAIL 被邀请参加人员的 Email 地址,中间以逗号分隔。
Events.RRULE 事件重复执行的规则。
Events.ACCESS_LEVEL 事件是私有的还是公开的。
Events.AVAILABILITY 事件是在忙时计时,还是空闲时计时。

下一节将介绍这些 Intent 的使用。

利用 Intent 插入事件

利用 INSERT Intent ,应用程序可以将事件插入工作交给 Calendar 来完成。 这样,就不需要在Manifest 文件中包含 WRITE_CALENDAR 权限。

当用户运行这类应用程序时,应用将会向 Calendar 发送 Intent 来完成事件添加操作。 INSERT Intent 利用其附加数据将事件信息填充到 Calendar 的表单中。 然后,用户可以根据需要取消事件、编辑表单数据,或者把事件保存到日历中。

下面给出了一段代码,在2012年1月1日安排一个事件,时间是上午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 编辑事件

正如 修改事件 一节所述,事件可以直接进行更新。但利用 EDIT Intent ,可以让不具备权限的应用将事件编辑工作交给 Calendar 应用来完成。 用户在 Calendar 中完成事件编辑之后,可以返回调用方应用。

下面的例子通过 Intent 设置某个事件的标题,并且用户可以在 Calendar 中编辑该事件。

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 查看日程数据

Calender Provider 提供了两种方式来使用 VIEW Intent:

  • 以指定日期打开 Calendar 应用
  • 查看事件

下面的例子演示了以指定日期打开 Calendar 的方式:

// 日期和时间以1970年1月1日以来的毫秒数给出
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);

Sync Adapter

应用程序和 Sync Adapter 在访问 Calendar Provider 时存在些许微小的差别:

  • Sync Adapter 需要声明其为 Sync Adapter 身份,把 CALLER_IS_SYNCADAPTER 设为 true 即可。
  • Sync Adapter 需要以 URI 参数的方式指定 ACCOUNT_NAME 和ACCOUNT_TYPE
  • Sync Adapter 有权限访问的字段比应用程序或 Widget 都要多一些。 例如,应用程序只能修改日程数据的一些表面性的属性,诸如名称、显示名称、是否可见、是否需要同步等。 相比之下,Sync Adapter 不仅能访问这些字段,还能访问诸如日历配色、时区、访问级别、地区等其他很多字段。 当然, Sync Adapter 访问 ACCOUNT_NAME 和 ACCOUNT_TYPE 是受限的。

下面是 Sync Adapter 可用于返回 URI 的助手方法:

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();
}

关于 Sync Adapter 的实例(与 Calendar 无关),请参阅 SampleSyncAdapter

Calendar Provider的更多相关文章

  1. ContentProvider官方教程(11)Calendar Provider、Contacts Provider、Storage Access Framework

    Calendar Provider: guide/topics/providers/calendar-provider.html Contacts Provider: guide/topics/pro ...

  2. Android开发-API指南- Calendar Provider

    Calendar Provider 英文原文:http://developer.android.com/guide/topics/providers/calendar-provider.html 采集 ...

  3. ContentProvider官方教程(9)定义一个provider完整示例:实现方法,定义权限等

    Creating a Content Provider In this document Designing Data Storage Designing Content URIs Implement ...

  4. Android开发-API指南-创建 Content Provider

    Creating a Content Provider 英文原文:http://developer.android.com/guide/topics/providers/content-provide ...

  5. Android开发-API指南-Content Provider基础

    Content Provider Basics 英文原文:http://developer.android.com/guide/topics/providers/content-provider-ba ...

  6. Android开发-API指南-Content Provider

    Content Providers 英文原文:http://developer.android.com/guide/topics/providers/content-providers.html 采集 ...

  7. Content Provider Basics ——Content Provider基础

    A content provider manages access to a central repository of data. A provider is part of an Android ...

  8. 我的Android 4 学习系列之数据库和Content Provider

    目录 创建数据库和使用SQLite 使用Content Provider.Cusor和Content Value来存储.共享和使用应用程序数据 使用Cursor Loader异步查询Content P ...

  9. ContentProvider官方教程(7)3种访问形式:批处理、异步访问、intent间接访问(临时URI权限)

    Alternative Forms of Provider Access Three alternative forms of provider access are important in app ...

随机推荐

  1. flash builder 4.6在debug调试时需要系统安装flashplayer debug版本

    http://blog.csdn.net/cupid0051/article/details/46684295

  2. 百度安卓sdk开发

    一 key问题 1 在百度地图api控制台申请key的流程主要用到了app包,开发工具的开发sha1和发布sha1值,这2个值的获取就非常关键了. 一般来说我们都是在windows上开发安卓,使用an ...

  3. seaj和requirejs模块化的简单案例

    如今,webpack.gulp等构件工具流行,有人说seajs.requirejs等纯前端的模块化工具已经被淘汰了,我不这么认为,毕竟纯前端领域想要实现模块化就官方来讲,还是有一段路要走的.也因此纯前 ...

  4. [剑指Offer] 44.翻转单词顺序列

    题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思.例如,“student ...

  5. c#调用系统默认软件打开应用

    System.Diagnostics.Process.Start(),参数为对应的应用路径 System.Diagnostics.Process.Start(((FileInfo)lv.Selecte ...

  6. Python 基本数据结构

    Python基本数据结构 数据结构:通俗点儿说,就是存储数据的容器.这里主要介绍Python的4种基本数据结构:列表.元组.字典.集合: 格式如下: 列表:list = [val1, val2, va ...

  7. [洛谷P5068][Ynoi2015]我回来了

    题目大意:给你一张$n(n\leqslant10^3)$个点$m(m\leqslant10^5)$个点的无向无权图,多组询问,每次询问给你一些二元组$(x_i,y_i)$,求有多少个$u$于至少一个二 ...

  8. 【Codeforces Round #404 (Div. 2)】题解

    A. Anton and Polyhedrons 直接统计+答案就可以了. #include<cstdio> #include<cstring> #include<alg ...

  9. [Leetcode] Binary tree level order traversal ii二叉树层次遍历

    Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left ...

  10. 第一次BC

    BestCoder Round #90 1001 Kblack loves flag 太弱只写了这一道水题. 首先这个题面就是,完全不知道它在说什么.开始5mins后我还完全不知道这个题想要表达什么. ...