日曆提供者是使用者日曆活動的存放區。您可以使用 Calendar Provider API 對日曆、活動、與會者、提醒事項等執行查詢、插入、更新和刪除作業。
應用程式和同步轉換介面可使用 Calendar Provider API。規則會因發出呼叫的程式類型而異。本文主要說明如何將 Calendar Provider API 用於應用程式。如要進一步瞭解同步轉換介面的差異,請參閱「同步轉換介面」。
通常,應用程式的資訊清單必須包含適當的權限,才能讀取或寫入日曆資料,詳情請參閱「使用者權限」。為了讓常見作業更容易執行,日曆提供者會提供一組意圖,如「日曆意圖」一文所述。這些意圖可將使用者帶往日曆應用程式,以便插入、查看及編輯活動。使用者與日曆應用程式互動,然後返回原始應用程式。因此,應用程式不需要要求權限,也不需要提供可查看或建立事件的使用者介面。
基本概念
內容供應器會儲存資料,並讓應用程式存取這些資料。Android 平台 (包括日曆提供者) 提供的內容提供者通常會將資料以一系列表格形式公開,這些表格是根據關聯式資料庫模型建立,其中每列都是一筆記錄,每欄則是特定類型和含義的資料。應用程式和同步轉換介面可透過 Calendar Provider API,取得含有使用者日曆資料的資料庫資料表讀取/寫入權限。
每個內容供應器都會公開用於識別資料集的公開 URI (包裝為 Uri
物件)。控制多個資料集 (多個資料表) 的內容供應器會為每個資料集公開個別的 URI。提供者的所有 URI 開頭都會是字串「content://」。這樣可將資料識別為內容供應器控管。日曆提供者會為其各個類別 (資料表) 定義 URI 常數。這些 URI 的格式為 <class>.CONTENT_URI
。例如:Events.CONTENT_URI
。
圖 1 顯示「日曆提供者」資料模型的示意圖。會顯示主要資料表以及這些資料表相互連結的欄位。
使用者可以擁有多個日曆,且不同日曆可與不同類型的帳戶 (Google 日曆、Exchange 等) 建立關聯。
CalendarContract
會定義日曆和活動相關資訊的資料模型。這項資料會儲存在下列多個資料表中:
Table (類別) | 說明 |
---|---|
此表含有日曆專屬資訊。這個資料表的每個資料列都包含單一日曆的詳細資料,例如名稱、顏色、同步處理資訊等。 | |
CalendarContract.Events |
這個表格會儲存事件專屬資訊。這個表格中的每一列都包含單一活動的資訊,例如活動標題、地點、開始時間、結束時間等等。事件可以發生一次,也可以重複發生多次。參與者、提醒和擴充屬性會儲存在不同的資料表中。每個事件都包含一個 EVENT_ID ,可參照「事件」表格中的 _ID 。 |
CalendarContract.Instances |
這個資料表會保存每個事件發生的開始和結束時間。這個表格中的每一列都代表單一事件發生情形。對於一次性事件,會將例項與事件進行 1:1 對應。如果是週期性事件,系統會自動產生多個資料列,這些資料列會對應至該事件的多次。 |
CalendarContract.Attendees |
這個資料表會儲存活動與會者 (嘉賓) 的資訊。每列都代表單一事件的一位邀請對象。這項資訊會指定邀請對象的類型,以及該邀請對象對活動的出席回覆。 |
CalendarContract.Reminders |
這個資料表會儲存警示/通知資料。每一列都代表一項事件的單一警示。一個活動可以有多個提醒。每個活動的提醒次數上限會在 MAX_REMINDERS 中指定,由擁有指定日曆的同步處理器設定。提醒時間會以事件發生前幾分鐘為單位指定,並提供方法來決定使用者收到的通知方式。 |
Calendar Provider API 的設計旨在提供靈活且強大的功能。同時,請務必提供良好的使用者體驗,並保護日曆及其資料的完整性。因此,使用 API 時,請注意下列事項:
- 插入、更新及查看日曆活動。如要直接在日曆供應器中插入、修改及讀取事件,您必須具備適當的權限。不過,如果您並未建構完整的日曆應用程式或同步處理轉接器,則不必要求這些權限。您可以改用 Android 日曆應用程式支援的意圖,將讀取和寫入作業交給該應用程式。使用意圖時,應用程式會將使用者傳送至日曆應用程式,在預先填妥的表單中執行所需作業。完成後,系統會將他們帶回您的應用程式。您可以設計應用程式,透過日曆執行常見作業,為使用者提供一致且健全的使用者介面。此為建議做法。詳情請參閱「日曆意圖」。
- 同步轉換介面同步轉換介面會將使用者裝置上的日曆資料與其他伺服器或資料來源同步處理。在
CalendarContract.Calendars
和CalendarContract.Events
資料表中,有幾個資料欄是供同步處理轉接器使用的。供應商和應用程式不應修改這些值。事實上,除非以同步處理轉接器的形式存取,否則這些資料不會顯示。如要進一步瞭解同步處理轉換介面,請參閱同步處理轉接程式。
使用者權限
如要讀取日曆資料,應用程式的資訊清單檔案必須包含 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>
日曆表
CalendarContract.Calendars
表格包含個別日曆的詳細資料。下列日曆資料欄可供應用程式和同步處理器寫入。如需支援欄位的完整清單,請參閱 CalendarContract.Calendars
參考資料。
常數 | 說明 |
---|---|
NAME |
日曆的名稱。 |
CALENDAR_DISPLAY_NAME |
向使用者顯示的這個日曆名稱。 |
VISIBLE |
布林值,表示是否要顯示該日曆。值為 0 表示不應顯示與此日曆相關聯的活動。如果值為 1,表示系統應顯示與此日曆相關聯的活動。這個值會影響 CalendarContract.Instances 資料表中資料列的產生方式。 |
SYNC_EVENTS |
布林值,表示是否要同步處理日曆,並將日曆的事件儲存在裝置上。值為 0 表示不要同步這份日曆或在裝置上儲存其活動。值為 1 表示要為這個日曆同步事件,並將事件儲存在裝置上。 |
為所有作業納入帳戶類型
如果您對 Calendars.ACCOUNT_NAME
進行查詢,則必須在選取範圍中加入 Calendars.ACCOUNT_TYPE
。這是因為系統會根據 ACCOUNT_NAME
和 ACCOUNT_TYPE
判斷帳戶是否唯一。ACCOUNT_TYPE
是與帳戶驗證器相對應的字串,該驗證器是在帳戶註冊至 AccountManager
時使用。另外,如果日曆未與裝置帳戶建立關聯,也會有一種稱為 ACCOUNT_TYPE_LOCAL
的特殊帳戶。ACCOUNT_TYPE_LOCAL
帳戶不會同步處理。
查詢日曆
以下範例說明如何取得特定使用者擁有的 Google 日曆。為了簡化說明,本例會在使用者介面執行緒 (「主執行緒」) 中顯示查詢作業。實際上,這項操作應在非同步執行緒中執行,而不是在主執行緒中執行。如需進一步討論,請參閱「載入器」。如果不只是讀取資料,而是要修改資料,請參閱 AsyncQueryHandler
。
Kotlin
// Projection array. Creating indices for this array instead of doing // dynamic lookups improves performance. private val EVENT_PROJECTION: Array<String> = arrayOf( CalendarContract.Calendars._ID, // 0 CalendarContract.Calendars.ACCOUNT_NAME, // 1 CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, // 2 CalendarContract.Calendars.OWNER_ACCOUNT // 3 ) // The indices for the projection array above. private const val PROJECTION_ID_INDEX: Int = 0 private const val PROJECTION_ACCOUNT_NAME_INDEX: Int = 1 private const val PROJECTION_DISPLAY_NAME_INDEX: Int = 2 private const val PROJECTION_OWNER_ACCOUNT_INDEX: Int = 3
Java
// 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;
在範例的後續部分,您將建構查詢。選取項目會指定查詢的條件。在這個範例中,查詢會尋找含有 ACCOUNT_NAME
「hera@example.com」的 Google 日曆、ACCOUNT_TYPE
「com.example」和 OWNER_ACCOUNT
「hera@example.com」。如果您想查看使用者查看的所有日曆 (而非僅查看使用者擁有的日曆),請省略 OWNER_ACCOUNT
。查詢會傳回 Cursor
物件,您可以使用該物件來遍歷資料庫查詢傳回的結果集。如要進一步瞭解如何在內容供應器中使用查詢,請參閱「內容供應器」。
Kotlin
// Run query val uri: Uri = CalendarContract.Calendars.CONTENT_URI val selection: String = "((${CalendarContract.Calendars.ACCOUNT_NAME} = ?) AND (" + "${CalendarContract.Calendars.ACCOUNT_TYPE} = ?) AND (" + "${CalendarContract.Calendars.OWNER_ACCOUNT} = ?))" val selectionArgs: Array<String> = arrayOf("hera@example.com", "com.example", "hera@example.com") val cur: Cursor = contentResolver.query(uri, EVENT_PROJECTION, selection, selectionArgs, null)
Java
// 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[] {"hera@example.com", "com.example", "hera@example.com"}; // Submit the query and get a Cursor object back. cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
下一個部分會使用遊標來瀏覽結果集。它會使用在示例開頭設定的常數,傳回每個欄位的值。
Kotlin
// Use the cursor to step through the returned records while (cur.moveToNext()) { // Get the field values val calID: Long = cur.getLong(PROJECTION_ID_INDEX) val displayName: String = cur.getString(PROJECTION_DISPLAY_NAME_INDEX) val accountName: String = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX) val ownerName: String = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX) // Do something with the values... }
Java
// 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... ... }
修改日曆
如要更新日曆,您可以將日曆的 _ID
以附加 ID 形式提供給 URI (withAppendedId()
),或做為第一個選取項目。所選項目應以 "_id=?"
開頭,而第一個 selectionArg
應為日曆的 _ID
。您也可以在 URI 中將 ID 編碼,以便進行更新。以下範例使用 (withAppendedId()
) 方法變更日曆的顯示名稱:
Kotlin
const val DEBUG_TAG: String = "MyActivity" ... val calID: Long = 2 val values = ContentValues().apply { // The new display name for the calendar put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar") } val updateUri: Uri = ContentUris.withAppendedId(CalendarContract.Calendars.CONTENT_URI, calID) val rows: Int = contentResolver.update(updateUri, values, null, null) Log.i(DEBUG_TAG, "Rows updated: $rows")
Java
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);
插入日曆
日曆主要是由同步轉換介面管理,因此建議您只將新日曆做為同步轉接器插入。在大多數情況下,應用程式只能對日曆進行表面上的變更,例如變更顯示名稱。如果應用程式需要建立本機日曆,可以使用 ACCOUNT_TYPE_LOCAL
的 ACCOUNT_TYPE
,以同步轉換介面執行日曆插入作業。ACCOUNT_TYPE_LOCAL
是一種特殊帳戶類型,用於未與裝置帳戶建立關聯的日曆。這類型日曆不會同步處理至伺服器。如要瞭解同步轉換介面,請參閱「同步轉換介面」。
事件資料表
CalendarContract.Events
表格包含個別事件的詳細資料。如要新增、更新或刪除事件,應用程式必須在資訊清單檔案中加入 WRITE_CALENDAR
權限。
下列「事件」欄可供應用程式和同步處理轉接器寫入。如需支援欄位的完整清單,請參閱 CalendarContract.Events
參考資料。
常數 | 說明 |
---|---|
CALENDAR_ID |
活動所屬日曆的 _ID 。 |
ORGANIZER |
活動主辦人 (擁有者) 的電子郵件地址。 |
TITLE |
活動的名稱。 |
EVENT_LOCATION |
事件發生的地點。 |
DESCRIPTION |
事件的說明。 |
DTSTART |
事件開始的時間,以毫秒為單位自 Epoch 紀元時間起算 (採用世界標準時間)。 |
DTEND |
事件結束的時間,以世界標準時間 (以毫秒為單位) 自 Epoch 紀元時間起算。 |
EVENT_TIMEZONE |
活動的時區。 |
EVENT_END_TIMEZONE |
活動結束時間的時區。 |
DURATION |
事件持續時間,採用 RFC5545 格式。
舉例來說,如果值為 "PT1H" ,表示事件應持續一小時,而值為 "P2W" 則表示持續時間為 2 週。 |
ALL_DAY |
如果值為 1,表示此活動會佔用整天,以當地時區定義。如果值為 0,表示這是一項定期事件,可能會在一天中的任何時間開始和結束。 |
RRULE |
活動格式的週期性規則。例如:"FREQ=WEEKLY;COUNT=10;WKST=SU" 。如需更多範例,請參閱這篇文章。 |
RDATE |
活動的週期日期。
您通常會將 RDATE 與 RRULE 搭配使用,定義一組重複出現的項目。如需進一步討論,請參閱 RFC5545 規格。 |
AVAILABILITY |
如果這個活動顯示為忙碌的時間或可預約的有空時間, |
GUESTS_CAN_MODIFY |
邀請對象是否可以修改活動。 |
GUESTS_CAN_INVITE_OTHERS |
邀請對象是否可以邀請其他人。 |
GUESTS_CAN_SEE_GUESTS |
邀請對象是否可查看與會者名單。 |
新增事件
應用程式插入新事件時,建議您使用 INSERT
意圖,如「使用意圖插入事件」所述。不過,您可以視需要直接插入事件。本節將說明如何執行這項操作。
插入新事件的規則如下:
- 您必須加入
CALENDAR_ID
和DTSTART
。 - 您必須加入
EVENT_TIMEZONE
。如要取得系統已安裝的時區 ID 清單,請使用getAvailableIDs()
。請注意,如果您是透過INSERT
意圖插入事件,則此規則不適用。如需相關說明,請參閱「使用意圖插入事件」一文。在這種情況下,系統會提供預設時區。 - 如為非週期性事件,則必須加入
DTEND
。 - 如果是週期性活動,除了
RRULE
或RDATE
以外,您還必須加入DURATION
。請注意,如果您是透過INSERT
意圖插入事件 (請參閱「使用意圖插入事件」),則不適用這項規則。在這種情況下,您可以將RRULE
與DTSTART
和DTEND
搭配使用,日曆應用程式會自動將其轉換為時間長度。
以下是插入事件的範例。為求簡單起見,這項操作會在 UI 執行緒中執行。實際上,插入和更新作業應在非同步執行緒中執行,以便將動作移至背景執行緒。詳情請參閱 AsyncQueryHandler
。
Kotlin
val calID: Long = 3 val startMillis: Long = Calendar.getInstance().run { set(2012, 9, 14, 7, 30) timeInMillis } val endMillis: Long = Calendar.getInstance().run { set(2012, 9, 14, 8, 45) timeInMillis } ... val values = ContentValues().apply { put(CalendarContract.Events.DTSTART, startMillis) put(CalendarContract.Events.DTEND, endMillis) put(CalendarContract.Events.TITLE, "Jazzercise") put(CalendarContract.Events.DESCRIPTION, "Group workout") put(CalendarContract.Events.CALENDAR_ID, calID) put(CalendarContract.Events.EVENT_TIMEZONE, "America/Los_Angeles") } val uri: Uri = contentResolver.insert(CalendarContract.Events.CONTENT_URI, values) // get the event ID that is the last element in the Uri val eventID: Long = uri.lastPathSegment.toLong() // // ... do something with event ID // //
Java
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。這是取得事件 ID 最簡單的方法。您通常需要活動 ID 才能執行其他日曆作業,例如為活動新增參與者或提醒。
更新事件
如果應用程式想要允許使用者編輯事件,建議您使用 EDIT
意圖,如「使用意圖編輯事件」一文所述。不過,您可以視需要直接編輯事件。如要更新事件,您可以提供事件的 _ID
,做為 URI (withAppendedId()
) 的附加 ID,或做為第一個選取項目。這個選項應以 "_id=?"
開頭,第一個 selectionArg
應為事件的 _ID
。您也可以使用沒有 ID 的選取項目進行更新。以下是更新事件的範例。它會使用 withAppendedId()
方法變更活動名稱:
Kotlin
val DEBUG_TAG = "MyActivity" ... val eventID: Long = 188 ... val values = ContentValues().apply { // The new title for the event put(CalendarContract.Events.TITLE, "Kickboxing") } val updateUri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID) val rows: Int = contentResolver.update(updateUri, values, null, null) Log.i(DEBUG_TAG, "Rows updated: $rows")
Java
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 = cr.update(updateUri, values, null, null); Log.i(DEBUG_TAG, "Rows updated: " + rows);
刪除活動
您可以透過 URI 上的附加 ID _ID
刪除事件,也可以使用標準選取項目。如果使用附加 ID,您也無法進行選取。刪除有兩種版本:分別是應用程式和同步處理轉換器。應用程式刪除作業會將「已刪除」欄設為 1。這個標記會告知同步處理器已刪除資料列,且應將這項刪除作業傳播至伺服器。同步轉換介面刪除事件會將該事件及所有相關資料從資料庫中移除。以下範例說明如何透過 _ID
刪除事件:
Kotlin
val DEBUG_TAG = "MyActivity" ... val eventID: Long = 201 ... val deleteUri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID) val rows: Int = contentResolver.delete(deleteUri, null, null) Log.i(DEBUG_TAG, "Rows deleted: $rows")
Java
private static final String DEBUG_TAG = "MyActivity"; ... long eventID = 201; ... ContentResolver cr = getContentResolver(); Uri deleteUri = null; deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); int rows = cr.delete(deleteUri, null, null); Log.i(DEBUG_TAG, "Rows deleted: " + rows);
與會者資料表
CalendarContract.Attendees
資料表的每一列都代表某活動的一位參與者或邀請對象。呼叫 query()
會傳回含有指定 EVENT_ID
的事件參與者清單。這個 EVENT_ID
必須與特定事件的 _ID
相符。
下表列出可寫入的欄位。插入新出席者時,您必須納入所有欄位 (ATTENDEE_NAME
除外)。
常數 | 說明 |
---|---|
EVENT_ID |
事件 ID。 |
ATTENDEE_NAME |
與會者的姓名。 |
ATTENDEE_EMAIL |
與會者的電子郵件地址。 |
ATTENDEE_RELATIONSHIP |
與會者與活動的關係。下列其中一個項目: |
ATTENDEE_TYPE |
與會者類型。下列其中一項: |
ATTENDEE_STATUS |
參與者的出席狀態。下列其中一個項目: |
新增與會者
以下是將單一與會者新增至活動的範例。請注意,EVENT_ID
為必填欄位:
Kotlin
val eventID: Long = 202 ... val values = ContentValues().apply { put(CalendarContract.Attendees.ATTENDEE_NAME, "Trevor") put(CalendarContract.Attendees.ATTENDEE_EMAIL, "trevor@example.com") put( CalendarContract.Attendees.ATTENDEE_RELATIONSHIP, CalendarContract.Attendees.RELATIONSHIP_ATTENDEE ) put(CalendarContract.Attendees.ATTENDEE_TYPE, CalendarContract.Attendees.TYPE_OPTIONAL) put( CalendarContract.Attendees.ATTENDEE_STATUS, CalendarContract.Attendees.ATTENDEE_STATUS_INVITED ) put(CalendarContract.Attendees.EVENT_ID, eventID) } val uri: Uri = contentResolver.insert(CalendarContract.Attendees.CONTENT_URI, values)
Java
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);
提醒事項資料表
CalendarContract.Reminders
資料表的每一列都代表某活動的一則提醒。呼叫 query()
會傳回具有指定 EVENT_ID
的事件提醒事項清單。
下表列出可寫入的提醒事項欄位。插入新提醒時,必須納入所有這些項目。請注意,同步處理轉接器會在 CalendarContract.Calendars
表格中指定支援的提醒類型。詳情請參閱 ALLOWED_REMINDERS
。
常數 | 說明 |
---|---|
EVENT_ID |
事件 ID。 |
MINUTES |
活動開始前應觸發提醒的時間 (分鐘)。 |
METHOD |
在伺服器上設定的鬧鐘方法。下列其中一項: |
新增提醒
這個範例會為活動新增提醒。提醒會在活動開始前 15 分鐘觸發。
Kotlin
val eventID: Long = 221 ... val values = ContentValues().apply { put(CalendarContract.Reminders.MINUTES, 15) put(CalendarContract.Reminders.EVENT_ID, eventID) put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT) } val uri: Uri = contentResolver.insert(CalendarContract.Reminders.CONTENT_URI, values)
Java
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);
執行個體資料表
CalendarContract.Instances
表格會保留事件發生的開始和結束時間。這個表格中的每一列都代表單一事件發生情形。執行個體表格無法寫入,只提供查詢事件發生情形的方式。
下表列出可針對某個執行個體進行查詢的部分欄位。請注意,時區是由 KEY_TIMEZONE_TYPE
和 KEY_TIMEZONE_INSTANCES
定義。
常數 | 說明 |
---|---|
BEGIN |
執行個體的開始時間,以世界標準時間為準。 |
END |
執行個體的結束時間,以世界標準時間毫秒為單位。 |
END_DAY |
以日曆時區為準的例項儒略結束日。 |
END_MINUTE |
從日曆時區午夜起算的例項結束分鐘。 |
EVENT_ID |
這個例項的事件 _ID 。 |
START_DAY |
以日曆時區為準,執行個體的儒略曆開始日。 |
START_MINUTE |
從午夜開始測量的執行個體開始時間,以日曆的時區為準。 |
查詢執行個體資料表
如要查詢「Instances」資料表,您必須在 URI 中指定查詢的時間範圍。在這個範例中,CalendarContract.Instances
會透過實作 CalendarContract.EventsColumns
介面,取得 TITLE
欄位的存取權。換句話說,TITLE
是透過資料庫檢視畫面傳回,而非透過查詢原始 CalendarContract.Instances
表格。
Kotlin
const val DEBUG_TAG: String = "MyActivity" val INSTANCE_PROJECTION: Array<String> = arrayOf( CalendarContract.Instances.EVENT_ID, // 0 CalendarContract.Instances.BEGIN, // 1 CalendarContract.Instances.TITLE // 2 ) // The indices for the projection array above. const val PROJECTION_ID_INDEX: Int = 0 const val PROJECTION_BEGIN_INDEX: Int = 1 const val PROJECTION_TITLE_INDEX: Int = 2 // Specify the date range you want to search for recurring // event instances val startMillis: Long = Calendar.getInstance().run { set(2011, 9, 23, 8, 0) timeInMillis } val endMillis: Long = Calendar.getInstance().run { set(2011, 10, 24, 8, 0) timeInMillis } // The ID of the recurring event whose instances you are searching // for in the Instances table val selection: String = "${CalendarContract.Instances.EVENT_ID} = ?" val selectionArgs: Array<String> = arrayOf("207") // Construct the query with the desired date range. val builder: Uri.Builder = CalendarContract.Instances.CONTENT_URI.buildUpon() ContentUris.appendId(builder, startMillis) ContentUris.appendId(builder, endMillis) // Submit the query val cur: Cursor = contentResolver.query( builder.build(), INSTANCE_PROJECTION, selection, selectionArgs, null ) while (cur.moveToNext()) { // Get the field values val eventID: Long = cur.getLong(PROJECTION_ID_INDEX) val beginVal: Long = cur.getLong(PROJECTION_BEGIN_INDEX) val title: String = cur.getString(PROJECTION_TITLE_INDEX) // Do something with the values. Log.i(DEBUG_TAG, "Event: $title") val calendar = Calendar.getInstance().apply { timeInMillis = beginVal } val formatter = SimpleDateFormat("MM/dd/yyyy") Log.i(DEBUG_TAG, "Date: ${formatter.format(calendar.time)}") }
Java
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())); } }
日曆意圖
應用程式不需要權限即可讀取及寫入日曆資料。而是可以使用 Android 日曆應用程式支援的意圖,將讀取和寫入作業交給該應用程式。下表列出日曆提供者支援的意圖:
動作 | URI | 說明 | 額外內容 |
---|---|---|---|
VIEW |
CalendarContract.CONTENT_URI 參照 URI。如需使用意圖的範例,請參閱「使用意圖查看日曆資料」。 |
開啟日曆,並將時間設為 <ms_since_epoch> 指定的時間。 |
無。 |
Events.CONTENT_URI 參照 URI。如需使用這個意圖的範例,請參閱「使用意圖查看日曆資料」。 |
查看 <event_id> 指定的事件。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
|
EDIT |
Events.CONTENT_URI 參照 URI。如需使用此意圖的範例,請參閱「使用意圖編輯事件」。
|
編輯 <event_id> 指定的事件。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
EDIT INSERT |
Events.CONTENT_URI 參照 URI。如需使用此意圖的範例,請參閱「使用意圖插入事件」。 |
建立活動。 | 下表列出的任何額外內容。 |
下表列出日曆供應器支援的意圖額外項目:
意圖額外資料 | 說明 |
---|---|
Events.TITLE |
事件的名稱。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME |
事件開始時間,以 Epoch 紀元時間起算的毫秒數為單位。 |
CalendarContract.EXTRA_EVENT_END_TIME |
事件結束時間,以 Epoch 紀元時間起算的毫秒數。 |
CalendarContract.EXTRA_EVENT_ALL_DAY |
布林值,指出活動是否為全天活動。值可以是 true 或 false 。 |
Events.EVENT_LOCATION |
活動的地點。 |
Events.DESCRIPTION |
活動說明。 |
Intent.EXTRA_EMAIL |
邀請對象的電子郵件地址,以半形逗號分隔。 |
Events.RRULE |
事件週期性規則。 |
Events.ACCESS_LEVEL |
活動是否為私人或公開活動。 |
Events.AVAILABILITY |
這項活動是否會計入忙碌時間,或是可預約的空閒時間。 |
以下各節將說明如何使用這些意圖。
使用意圖插入事件
使用 INSERT
意圖,可讓應用程式將活動插入工作傳送給日曆本身。採用這種做法,應用程式甚至不需要在資訊清單檔案中加入 WRITE_CALENDAR
權限。
當使用者執行採用這種做法的應用程式時,應用程式會將使用者傳送至日曆,以便完成新增活動。INSERT
意圖會使用額外欄位,在日曆中預先填入表單的活動詳細資料。使用者接著可以取消活動、視需要編輯表單,或將活動儲存到日曆。
以下程式碼片段會排定 2012 年 1 月 19 日上午 7:30 到 8:30 執行事件。請注意這個程式碼片段的相關事項:
- 並將
Events.CONTENT_URI
指定為 Uri。 - 它會使用
CalendarContract.EXTRA_EVENT_BEGIN_TIME
和CalendarContract.EXTRA_EVENT_END_TIME
額外欄位,在表單中預先填入事件時間。這些時間的值必須以世界標準時間 (UTC) 毫秒為單位,從 Epoch 紀元時間起算。 - 它會使用
Intent.EXTRA_EMAIL
額外欄位,提供以半形逗號分隔的邀請對象清單,並以電子郵件地址指定。
Kotlin
val startMillis: Long = Calendar.getInstance().run { set(2012, 0, 19, 7, 30) timeInMillis } val endMillis: Long = Calendar.getInstance().run { set(2012, 0, 19, 8, 30) timeInMillis } val intent = Intent(Intent.ACTION_INSERT) .setData(CalendarContract.Events.CONTENT_URI) .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startMillis) .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endMillis) .putExtra(CalendarContract.Events.TITLE, "Yoga") .putExtra(CalendarContract.Events.DESCRIPTION, "Group class") .putExtra(CalendarContract.Events.EVENT_LOCATION, "The gym") .putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY) .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com") startActivity(intent)
Java
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);
使用意圖編輯事件
您可以直接更新事件,如「更新事件」一文所述。不過,使用 EDIT
意圖可讓沒有權限的應用程式將活動編輯權限交給日曆應用程式。使用者完成日曆活動編輯作業後,就會返回原本的應用程式。
以下的意圖範例會設定指定活動的新標題,並讓使用者在日曆中編輯活動。
Kotlin
val eventID: Long = 208 val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID) val intent = Intent(Intent.ACTION_EDIT) .setData(uri) .putExtra(CalendarContract.Events.TITLE, "My New Title") startActivity(intent)
Java
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);
使用意圖查看日曆資料
日曆提供者提供兩種使用 VIEW
意圖的方式:
- 如要開啟日曆並前往特定日期。
- 查看事件。
以下範例說明如何開啟日曆,並將日期設為特定日期:
Kotlin
val startMillis: Long ... val builder: Uri.Builder = CalendarContract.CONTENT_URI.buildUpon() .appendPath("time") ContentUris.appendId(builder, startMillis) val intent = Intent(Intent.ACTION_VIEW) .setData(builder.build()) startActivity(intent)
Java
// 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);
以下範例說明如何開啟事件供查看:
Kotlin
val eventID: Long = 208 ... val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID) val intent = Intent(Intent.ACTION_VIEW).setData(uri) startActivity(intent)
Java
long eventID = 208; ... Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); Intent intent = new Intent(Intent.ACTION_VIEW) .setData(uri); startActivity(intent);
同步轉換介面
應用程式和同步處理器存取日曆提供者的做法只有細微差異:
- 同步轉換介面必須將
CALLER_IS_SYNCADAPTER
設為true
,以指定其為同步轉換介面。 - 同步轉換介面必須在 URI 中提供
ACCOUNT_NAME
和ACCOUNT_TYPE
做為查詢參數。 - 同步處理器的寫入權限比應用程式或小工具更廣泛,舉例來說,應用程式只能修改日曆的部分特徵,例如名稱、顯示名稱、瀏覽權限設定,以及日曆是否同步。相較之下,同步處理轉接程式不僅可以存取這些欄,還可以存取許多其他欄,例如日曆顏色、時區、存取層級、位置等等。不過,同步處理轉換器僅限於指定的
ACCOUNT_NAME
和ACCOUNT_TYPE
。
以下是可用來傳回 URI 的輔助方法,可用於與同步轉換介面搭配使用:
Kotlin
fun asSyncAdapter(uri: Uri, account: String, accountType: String): Uri { return uri.buildUpon() .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true") .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, account) .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, accountType).build() }
Java
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(); }