第三部分:Android 应用程序接口指南---第一节:应用程序组件---第三章3-3日历provider
第3-3章 日历provider
日历provider是用来存放用户日历事件的一个仓库。日历provider的API会允许你执行对日历、事件、Attendees(与会者)和提醒的查询、更新和删除操作。日历provider的API可以被应用程序和同步adapter(适配器)使用。规则会因执行调用的程序类型而有所不同。这篇文章主要集中介绍作为一个应用程序如何来使用的日历provider。一般来说,要读取或写入日历数据,应用程序的manifest文件就必须把合适的权限包含进去。为了更简单地执行一般操作,日历provider会提供一组intents。这些intents可以让用户进入到日历应用程序,以便插入、查看和编辑事件。用户会与日历provider进行交互,并且返回到原来的应用程序。这样,你的应用程序就不需要请求权限,也不要提供一个用户界面来查看或创建事件。
3-3.1 基础知识
Content provider会储存数据,并使其对应用程序是可访问的。由Android平台提供的content provider(包括日历provider)通常会把数据作为一组表的集合暴露出来,这个表集合基于关系型数据库模型,其中每一行代表一个记录,每一列代表一个特定类型和有特定含义的数据。通过日历provider的API,应用程序和同步adapter(适配器)可以获得对数据库表的读/写访问权,这个数据库表持有用户的日历数据。
每个content provider都会暴露一个唯一指定它数据集的公有URI(作为一个Uri对象封装)。一个控制多个数据集的content provider会为每一个表暴露一个单独的URI。Provider中的所有URI都是以字符串“content://”开头。这就表明数据正被一个content provider控制。日历provider会为它的每一个类定义URI常量。这些URI的格式是<class>.CONTENT_URI。例如,Events.CONTENT_URI。
下面的图3-3-1展示了一个日历provider的数据模型。它展示了主要的表和互相链接的域。
图3-3-1:日历provider的数据模型
用户可以有多个日历,并且不同的日历会与不同类型的账号相关联(Google日历等)。CalendarContract类会定义日历的数据模型和事件的相关信息,这个数据会出现在多个表中,如表3-3-1所示:
表 (类) |
描述 |
CalendarContract.Calendars |
这个表持有特定日历的信息。这个表的每一行都包含一个单一日历的详细信息,如名称、颜色、同步信息等。 |
CalendarContract.Events |
这个表持有特定事件的信息。这个表的每一行都有单一事件的信息,如事件的标题、地点、开始时间、结束时间等。事件可以发生一次,也可以发生多次。与会者、提醒和继承属性都会被储存在单独的表中。他们都有一个引用事件表中_ID的EVENT_ID。 |
CalendarContract.Instances |
这个表持有事件每次发生的开始时间和结束时间。这个表中的每一行都代表一个发生的单一事件。对于一次性事件来说,这有一个1:1来映射事件的实例。对于周期事件,它会自动生成多个行来对应那个事件的多次发生。 |
CalendarContract.Attendees |
这个表持有事件attendees(与会者)(客人)的信息。行代表事件的单个客人。它指定客人的类型和参与响应事件的客人。 |
CalendarContract.Reminders |
这个表持有警告/通知数据。每一行代表一个单一的警告事件。一个事件可以有多个提醒。每个事件最大提醒数量可以用MAX_REMINDERS来指定,它由拥有给定日历的同步adapter(适配器)设置。提醒会在事件前几分钟内被指定,并且能决定用户如何被闹醒。 |
表3-3-1:日历provider的数据表摘要
日历provider的API被设计成是灵活和强大的。并且,重要的是要提供一个良好终端用户体验并保护日历数据完整性。为了这个终端,你在使用这些API时要记住下面几点:
(1) 插入、更新和查看日历事件:为了直接插入、修改和读取日历provider中的数据,你需要合适的权限。但是,如果你不要构建一个正式成熟的日历应用程序或同步adapter(适配器),那么请求这些权限是没必要的。相反,你可以使用Android日历应用程序支持的intents来处理对应用程序的读取和写入操作。当你使用intents时,你的应用程序会让用户进入到日历provider中,这样用户可以用预先填好的表单来执行想要的操作。在他们完成操作后,用户会返回到你的应用程序。通过设计执行一般日历操作的应用程序,你可以向用户提供一个一致的、强大的用户体验。这个是官方建议的方法。
(2) 同步adapters(适配器):一个同步adapter(适配器)会使得用户设备与另一个服务器或数据源上的日历数据同步。在CalendarContract.Calendars和CalendarContract.Events表中,有一些列被保留下来,供同步adapter(适配器)使用。Provider和应用程序不应该修改他们。实际上,他们是不可见的,除非他们是作为一个同步adapter(适配器)被访问才可见。
3-3.2 用户权限
为了读取日历数据,应用程序必须在它的manifest文件中包含READ_CALENDAR权限。它必须包含WRITE_CALENDAR权限用来删除、插入或更新日历数据,如代码清单3-3-1所示:
<?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>
代码清单3-3-1
3-3.3 日历表
CalendarContract.Calendars表包含单个日历的详细信息。下面表3-3-2中的日历列是由应用程序和同步adapter(适配器)写入的:
常量 |
描述 |
NAME |
日历名 |
CALENDAR_DISPLAY_NAME |
展示给用户的日历名称。 |
VISIBLE |
它是一个表示被选中日历是否要被展示的值。0值表示关联这个日历的事件不应该展示出来。而1值则表示关联这个日历的事件应该被展示出来。这个值会影响CalendarContract.instances表中的生成行。 |
SYNC_EVENTS |
它是一个表示日历是否应该被同步和是否应该把它的事件保存到设备上的值。0值表示不要同步这个日历或者不要把它的事件存储到设备上。1值则表示要同步这个日历的事件并把它的事件储存到设备上。 |
表3-3-2:
1.查询一个日历
下面是一个示例,它展示了如何获取特定用户拥有的日历,为了简单起见,在这个例子中,查询操作是出现在用户UI线程中(“main线程”)。实际上,这个应该是在异步线程上完成,而不是在主线程,如代码清单3-3-2所示:
// Projection 数组 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;
代码清单3-3-2
这个示例接下来是你要构建你的查询。选择指定查询的条件。在这个例子中,查询会找到拥有ACCOUNT_NAME“sampleuser@google.com”、ACCOUNT_TYPE”com.google”和OWNER_ACCOUNT”sampleuser@google.com”的日历。如果你想看到用户已经查看过的所有日历,而不是用户拥有的日历,那么你省略掉OWNER_ACCOUNT的查询。查询会返回一个Cursor对象,你可以用它来遍历由数据库查询返回的结果集合,如代码清单3-3-3所示:
//执行查询 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);
代码清单3-3-3
下面的代码表示的是用cursor来遍历结果集。它会用先前设置的常量来返回每个字段的值,如代码清单3-3-4所示:
// 使用cursor获得返回的每一条记录 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); //使用这些值做些什么... ... }
代码清单3-3-4
小讨论:为什么你必须包含ACOUNT_COUNT?
如果你查询Calendar.ACCOUNT_NAME,那么你必须在选择中包含Calendars.ACCOUNT_TYPE。这是因为一个给定的账号对于给定它的ACCOUNT_NAME和ACCOUNT_TYPE来说,它被认为是唯一的。当账号用AccountManager注册时,ACCOUNT_TYPE是相对账号身份验证的字符串。这也有一个名叫ACCOUNT_TYPE__LOCAL的特殊账号类型,它用于日历,不与设备账号相关联。ACCOUNT_TYPE-LOCAL账号不会被同步。
2.修改一个日历
为了执行一个日历的更新操作,你要么提供日历的_ID,把它追加到Uri的后面(用withAppendedId()方法),作为追加ID,要么把它作为第一个选择的item。选择语句应该以”_id=?”开头,并且第一个selectionArg应该是日历的_ID。你也可以通过把URI中的ID编码,来执行更新操作。下面我们就用(withAppendedId())方法来改变日历的显示名称,如代码清单3-3-5所示:
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);
代码清单3-3-5
3.插入一个日历
日历被设计成是主要被一个同步adapter(适配器)管理,所以你只需要插入作为一个同步adapter(适配器)的新日历。对日历的大部分来说,应用程序只会对它的表面做一些改变,如显示的名称。如果应用程序需要创建一个本地日历,那么它可以用一个ACCOUNT_TYPE_LOCAL的ACCOUNT_TYPE,通过执行日历作为同步adapter(适配器)的插入操作来完成对日历的创建。ACCOUNT_TYPE_LOCAL是日历的一个特殊账号类型,它没有与设备账号相关联。这个类型的日历不会与服务器同步。
3-3.4 事件表
CalendarContract.Events表包含单个事件的详细信息。为了添加、更新或删除事件,应用程序必须在它的manifest文件中包含WRITE_CALENDAR权限。下面的事件列是由应用程序和同步adapter(适配器)写入,如表3-3-3所示:
常量 |
描述 |
CALENDAR_ID |
这个是属于事件的日历_ID。 |
ORGANIZER |
电子邮件组织者(拥有者)的事件。 |
TITLE |
事件标题。 |
EVENT_LOCATION |
事件发生的地点。 |
DESCRIPTION |
事件的描述 |
DTSTART |
从公元纪年算起,以UTC毫秒为单位,事件开始的时间。 |
DTEND |
从公元纪年算起,以UTC毫秒为单位,事件结束的时间。 |
EVENT_TIMEZONE |
事件的时区。 |
EVENT_END_TIMEZONE |
事件结束的时区. |
DURATION |
事件的持续时间,用RFC5545格式表示。例如,“PT1H”值表示事件应该持续一个小时,“P2W”则表示持续2周。 |
ALL_DAY |
值1表示这个事件占了整整一天的时间,正如本地时区定义的那样。0值则表示这个是一次常规事件,它的开始时间和结束时间可能是这一天的任意时候。 |
RRULE |
事件格式的循环规则。例如,“FREQ=WEEKLY;COUNT=10;WKST=SU” |
RDATE |
事件的循环日期。你通常可以和RRULE一起使用来定义一个重复发生的总集合。详细描述请参考RFC5545 |
AVAILABILITY |
这个表示可以放入日程的事件是用繁忙时间还是空闲时间。 |
GUESTS_CAN_MODIFY |
来宾是否可以修改事件。 |
GUESTS_CAN_INVITE_OTHERS |
这个表示来宾是否可以邀请其他来宾。 |
GUESTS_CAN_SEE_GUESTS |
这个表示客户是否可以查看attendees(与会者)列表。. |
表3-3-3:事件表数据的摘要
1.添加事件
当你的应用程序要插入一个新事件时,我们建议你用一个intent.ACTION_INSERT。然而,如果你需要这样做,你可以直接插入事件。下面我们就详细介绍如何插入事件。
下面是插入一个新事件规则:
◆ 你必须包含CALENDAR_ID和DTSTART。
◆ 你必须包含一个EVENT_TIMEZONE。为了获得系统安装时区IDs的列表,你可以用gatAvailableIDs()方法。注意,如果你正在通过intent.ACTION_INSERT来插入一个事件,那么这个规则不会应用,在这种情况下,默认时区将被应用。
◆ 对于非循环事件,你必须包含DTEND。
◆ 对循环事件来说,你必须在RRULE和RDATE的基础上包含一个DURATION。注意,如果你正在通过intent.ACTION_INSERT来插入事件,那么这个规则不会应用——在这种情况下,你可以使用一个组合了DTSTART和DTEND的RRULE,并且日历应用程序会自动的把它转换成持续时间。
下面是一个插入事件的示例。它正在UI线程上被简单的执行。实际上,插入和更新操作应该在一个异步线程上执行,然后把动作移到一个后台线程,如代码清单3-3-6所示:
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); long eventID = Long.parseLong(uri.getLastPathSegment());
代码清单3-3-6
注意:你要看下这个示例是如何在事件被创建后捕获事件的ID。这是获取事件ID最简单的方式。你经常需要这个事件ID来执行其他日历操作,例如,把attendees(与会者)或提醒添加到事件中。
2.更新事件
当你的应用程序想允许用户来编辑事件时,我们建议你用一个intent.ACTION_EDIT。然而,如果你需要这样做,你也可以直接编辑事件。为了执行对事件的更新操作,你要么提供事件的_ID,把它追加到Uri后面(用withAppendedId()方法),作为一个追加ID,要么把它作为第一个选择的item。选择语句应该以“_id=?”开头,并且第一个selectionArg应该是事件的_ID。你也可以用一个不带ID的选择来更新事件。下面是一个更新事件的例子。它用withAppendedId()方法来改变事件的标题,如代码清单3-3-7所示:
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"); myUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); int rows = getContentResolver().update(updateUri, values, null, null); Log.i(DEBUG_TAG, "Rows updated: " + rows);
代码清单3-3-7
3.删除事件
你可以通过把事件的_ID作为URI上的追加ID来删除一个事件,也可以通过使用一个标准的selection来删除事件。如果你是用一个追加ID来删除事件,那么你就不能用selection。这有两种删除的版本:作为一个应用程序和一个同步adapter(适配器)。一个应用程序会把要删除的列设置为1。这个标记是告诉同步adapter(适配器),这个行已经被删除,并且这个删除应该被传到服务器上。一个同步adapter(适配器)的删除会把数据库中的事件和它所有的相关数据都移除。下面是应用程序通过事件的_ID来删除一个事件的例子,如代码清单3-3-8所示:
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);
代码清单3-3-8
3-3.5 Attendees(与会者)表
CalendarContract.Attendees表中的每一行都代表事件的一个单一attendee(与会者)或客户。调用query()方法会返回一个带有给定EVENT_ID事件attendee(与会者)的列表。这个EVENT_ID必须与一个特定事件的_ID相匹配。下面这个表列出了可写的域。当插入一个新的attendee(与会者)时,你必须包含下面表中的所有常量,除了ATTENDEE_NAME,如表格3-3-4所示:
常量 |
描述 |
EVENT_ID |
事件的ID。 |
ATTENDEE_NAME |
Attendees(与会者)的名称。 |
ATTENDEE_EMAIL |
Attendees(与会者)的电子邮件地址。 |
ATTENDEE_RELATIONSHIP |
事件attendees(与会者)的关系,包括: RELATIONSHIP_ATTENDEE RELATIONSHIP_NONE RELATIONSHIP_ORGANIZER RELATIONSHIP_PERFORMER RELATIONSHIP_SPEAKER |
ATTENDEE_TYPE |
Attendees(与会者)类型,包括: TYPE_REQUIRED TYPE_OPTIONAL |
ATTENDEE_STATUS |
事件attendees(出席者)的参加状态,包括: ATTENDEE_STATUS_ACCEPTED ATTENDEE_STATUS_DECLINED ATTENDEE_STATUS_INVITED ATTENDEE_STATUS_NONE ATTENDEE_STATUS_TENTATIVE |
表3-3-4:事件attendees(出席者)表
1. 添加与会人
下面是一个添加一个attendees(与会者)到事件的例子,注意,EVENT_ID是必需的,如代码清单3-3-9所示:
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);
代码清单3-3-9
3-3.6 提醒表
Calendar.Contract.Reminders表中的每一行都代表事件的单个提醒。调用query()方法会返回一个带有给定EVENT_ID的事件提醒列表。下面这个表列出了提醒的可写域。当插入一个新提醒时,表中的全部常量都要包含。注意,同步adapter(适配器)会指定他们在CalendarContract.Calendars表中支持的提醒类型,如表3-3-5所示:
常量 |
描述 |
EVENT_ID |
事件的ID。 |
MINUTES |
在事件发生前应该提醒的分钟。 |
METHOD |
正如在服务器上设置的闹钟方法,包括: METHOD_ALERT METHOD_DEFAULT METHOD_EMAIL METHOD_SMS |
表3-3-5:事件提醒表
1. 添加提醒
下面是添加一个提醒到事件的例子。提醒会在事件发生前持续响15分钟,如代码清单3-3-10所示:
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);
代码清单3-3-10
3-3.7 实例表
CalendarContract.Instances表会持有发生事件的开始和结束时间。在表中的每一行都代表一个单一事件的发生。实例表不能写入,并且只提供一种方法来查询事件的发生。下面的表列出了你可以查询一些实例所基于的域。注意,时区是由KEY_TIMEZONE_TYPE和KEY_TIMEZONE_INSTANCES定义,如表3-3-6所示:
常量 |
描述 |
BEGIN |
实例的开始时间,以UTC毫秒为单位。 |
END |
实例的结束时间,以UTC毫秒为单位。 |
END_DAY |
实例的Julian(Julian Calendar公历)结束时间,相对于日历时区。 |
END_MINUTE |
实例的结束分钟,从日历时区的0点算起。 |
EVENT_ID |
这个实例的事件_ID。 |
START_DAY |
实例的Julian(Julian Calendar公历)开始时间,相对于日历时区。 |
START_MINUTE |
实例的开始分钟,从日历时区的0点算起。 |
表3-3-6:事件实例表的数据摘要
1. 查询实例表
要查询实例表,你需要在URI中为查询指定范围时间。在这个例子中,CalendarContract.Instances会通过对CalendarContract.Eventscolumns接口的实现来获得对TITLE域的访问权。换句话说,TITLE域会通过一个数据库view来返回,而不是通过查询CalendarContract.Instances表中的行,如代码清单3-3-11所示:
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(); 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())); } }
代码清单3-3-11
3-3.8 日历intents
你的应用程序不需要permission来读取和写入日历数据。相反,它可以用Android日历应用程序支持的intents来把读写操作传递给应用程序。下面这个表就列出了日历provider支持的intents,如表3-3-7所示:
动作 |
URI |
描述 |
额外数据 |
|
content://com.android.calendar/time/<ms_since_epoch> 你可以用CalendarContract.CONTENT_URI来引用这个URI。 |
打开由<ms_since_epoch>指定时间的日历。 |
无 |
VIEW |
content://com.android.calendar/events/<event_id> 你也可以用Events.CONTENT_URI来引用这个URI。 |
查看由<event_id>指定的事件。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME |
EDIT |
content://com.android.calendar/events/<event_id> 你也可以用Events.CONTENT_URI.来引用这个URI。 |
编辑由<event_id>指定的事件。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME |
EDIT |
content://com.android.calendar/events 你也可以用Events.CONTENT_URI.来引用URI。 |
创建一个事件。 |
列举在下面表中的任意额外数据 |
表3-3-7:日历intent表
下面这个表列出了日历provider支持的intent额外数据,如表3-3-8所示:
Intent的额外数据 |
数据 |
Events.TITLE |
事件的名称。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME |
从公元纪年算起,事件的开始时间,以毫秒为单位。 |
CalendarContract.EXTRA_EVENT_END_TIME |
从公元纪年算起,事件的结束时间,以毫秒为单位。 |
CalendarContract.EXTRA_EVENT_ALL_DAY |
它是一个boolean值,表示事件会持续一整天。值可以是true或false。 |
Events.EVENT_LOCATION |
事件地点 |
Events.DESCRIPTION |
事件描述。 |
Intent.EXTRA_EMAIL |
作为一个逗号分隔列表,邀请人的电子邮件地址。 |
Events.RRULE |
事件的循环规则。 |
Events.ACCESS_LEVEL |
事件是私有还是公开的。 |
Events.AVAILABILITY |
这个事件是算为可以按计划进行的繁忙时间还是空闲时间。 |
表3-3-8:日历intent的额外数据表
下面我们就介绍如何使用这些intents。
1.使用一个intent来插入事件
使用intent.ACTION_INAERT让你的应用程序给日历传递插入事件。用这种方法,你的应用程序甚至不需要在manifest文件中包含WRITE_CALENDAR权限。当用户运行一个使用这个方法的应用程序时,应用程序会把他们发送到日历上,以便完成添加事件的任务。Intent.ACTION_INSERT会使用额外的域,以便预先生成一个带有日历事件详细信息的表单。然后,用户可以取消事件、在需要时编辑表单或者把事件保存到它们的日历中。下面是一段代码,它表示发生在2012年1月19日的计划事件,它是在上午7:30到上午8:30之间运行。注意,这个代码片段还有下面的问题;
◆ 它指定Events.CONTENT_URI作为Uri。
◆ 它会用CalendarContract.EXTRA_BEGIN_TIME和CalendarContract.EXTRA_EVENT_END_TIME额外域来预先生成一个带有事件时间的表单。这些时间的值必须是从公元纪年开始算起,并以UTC毫秒为单位。
◆ 它用intent.EXTRA_EMAIL额外域来提供一个逗号分隔的attendees(出席者)列表,它由电子邮件地址指定。下面是代码清单3-3-12:
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);
代码清单3-3-12
2.使用一个intent来编辑事件
你可以直接更新一个事件。但是,用intent.ACTION_EDIT会允许一个没有权限的应用程序把事件编辑传递给日历应用程序。当用户完成对日历事件的编辑时,他们就会返回原来的应用程序。下面是一个intent例子,这个intent可以为指定事件设置一个新标题并让用户编辑日历中的事件,如代码清单3-3-13所示:
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);
代码清单3-3-13
3.使用intents来查看日历数据
日历provider会提供两种使用VIEW intent的不同方法:
◆ 将日历打开到特定的日期
◆ 查看事件
下面是一个例子,它显示了如何把日历打开到特定日期,如代码清单3-3-14所示:
// 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);
代码清单3-3-14
下面是一个例子,它展示如何打开一个日历,以便查看事件,如代码清单3-3-15所示;
long eventID = 208; ... Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); Intent intent = new Intent(Intent.ACTION_VIEW) .setData(uri); startActivity(intent);
代码清单3-3-15
3-3.9 同步适配器(adapters)
应用程序和同步adapter(适配器)在访问日历provider上只有很小的区别:
◆ 同步adapter(适配器)需要通过设置CALLER_IS_SYNCADAPTER为true,才能说明它是同步adapter(适配器)。
◆ 同步adapter(适配器)需要提供一个ACCOUNT_NAME和ACCOUNT_TYPE作为URI中的查询参数。
◆ 同步adapter(适配器)比应用程序或widget拥有更多对列的写入访问权。例如,应用程序只能修改日历的少量特性,如它的名称、显示名称、可见设置和日历是否要同步。经过比较,同步adapter(适配器)可以访问的不只是那些列,还有其他许多东西,如日历的颜色、时区、访问级别和地点。然而,同步adapter(适配器)会被它指定的ACCOUNT_NAME和ACCOUNT_TYPE限制。
下面是一个辅助方法,你可以用它来返回一个URI,以便供同步adapter(适配器)使用,如代码清单3-3-16所示:
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(); }
代码清单3-3-16
本文来自jy02432443,QQ78117253。是本人辛辛苦苦一个个字码出来的,转载请保留出处,并保留追究法律责任的权利