android Calendar Provider
英文原文: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.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 插入事件 一节中将会介绍。 不过在必要时,也可以直接插入一条事件记录。本节将介绍这种方式。
下面列出了插入新事件需要遵守的规则:
- 必须包含
CALENDAR_ID
和DTSTART
。 - 必须包含
EVENT_TIMEZONE
。 利用getAvailableIDs()
可以读取系统已有时区的全部 ID。 请注意,如果是通过INSERT
来插入事件的,则本条规则不适用。 正如 利用 Intent 插入事件 一节所述,这时会使用默认的时区。 - 对于那些不需要重复触发的事件,必须包含
DTEND
。 - 对于要重复发生的事件,必须包含
DURATION
及RRULE
,或者是RDATE
。 请注意,如果是通过INSERT
来插入事件的,则本条规则不适用。 正如 利用 Intent 插入事件 一节所述,这时可使用RRULE
及DTSTART
、DTEND
的组合, Calendar 应用会自动转换为持续时间。
下面给出一个插入事件的例子。简化起见,此例运行于 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 |
CalendarContract.CONTENT_URI 来引用该 URI。关于该 Intent 的使用实例,请参阅 使用 Intent 查看日程数据。 |
打开日历,时间由<ms_since_epoch> 指定。 |
无 |
VIEW |
Events.CONTENT_URI 来引用该 URI。关于该 Intent 的使用实例,请参阅 使用 Intent 查看日程数据。 |
查看 <event_id> 指定的事件。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
EDIT |
content://com.android.calendar/events/<event_id> 也可以用 Events.CONTENT_URI . 来引用该 URI。关于该 Intent 的使用实例,请参阅 使用 Intent 编辑日程数据。 |
编辑 <event_id> 指定的事件。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
EDIT INSERT |
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 |
布尔值,标明事件是否占用一整天。值为true 或false 。 |
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。 请留意代码中的以下内容:
- 用
Events.CONTENT_URI
定义了 Uri。 - 用附加字段
CalendarContract.EXTRA_EVENT_BEGIN_TIME
andCalendarContract.EXTRA_EVENT_END_TIME
预置了事件的事件。单位必须是 UTC 1970年1月1日以来的毫秒数。 - 用附加字段
Intent.EXTRA_EMAIL
给出了逗号分割的参加人员列表,形式为 Email 地址。
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。