第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。是本人辛辛苦苦一個個字碼出來的,轉載請保留出處,並保留追究法律責任的權利