日曆供應程式總覽

日曆提供者是使用者日曆活動的存放區。您可以使用 Calendar Provider API 對日曆、活動、與會者、提醒事項等執行查詢、插入、更新和刪除作業。

應用程式和同步轉換介面可使用 Calendar Provider API。規則會因呼叫的程式類型而異。本文主要說明如何將 Calendar Provider API 用於應用程式。如要進一步瞭解同步轉換介面的差異,請參閱「同步轉換介面」。

通常,應用程式的資訊清單必須包含適當的權限,才能讀取或寫入日曆資料,詳情請參閱「使用者權限」。Google 日曆可協助您輕鬆執行常見作業 提供者提供一組意圖,如「Google 日曆」所述 意圖。這些意圖會將使用者導向 Google 日曆應用程式,以便插入、查看、 和編輯事件使用者與日曆應用程式互動,然後返回原始應用程式。因此應用程式不需要要求權限 也不需要提供使用者介面來查看或建立事件。

基本概念

內容供應器會儲存資料供使用者存取 應用程式。Android 平台 (包括「日曆供應商」) 提供的內容供應器通常會根據 關聯資料庫模型,其中每個資料列都是記錄,每欄為 屬於特定類型和意義透過日曆提供者 API,應用程式和同步處理器就能取得資料庫表格的讀/寫存取權,這些資料庫表格會儲存使用者的日曆資料。

每個內容供應器都會公開一個公開 URI (包裝成 Uri 物件),專門用於識別資料集。控制多個資料集 (多個資料表) 的內容供應器會為每個資料集公開個別的 URI。提供者的所有 URI 開頭都會是字串「content://」。這個 指出資料由內容供應者控管。日曆提供者會為其各個類別 (資料表) 定義 URI 常數。這些 URI 的格式為 <class>.CONTENT_URI。適用對象 例如:Events.CONTENT_URI

圖 1 為日曆供應器資料模型的示意圖。這張圖表會顯示主要資料表,以及連結這些資料表的欄位。

日曆供應程式資料模型

圖 1. 日曆供應程式資料模型。

使用者可以擁有多個日曆,且不同日曆可與不同類型的帳戶 (Google 日曆、Exchange 等) 建立關聯。

CalendarContract 定義日曆和事件相關資訊的資料模型。這些資料會儲存在下表,

Table (類別) 說明

CalendarContract.Calendars

這個資料表會保留特定日曆的資訊。這個資料表的每個資料列都包含單一日曆的詳細資料,例如名稱、顏色、同步處理資訊等。
CalendarContract.Events 這個表格包含 事件專屬資訊這個表格中的每一列都包含單一活動的資訊,例如活動名稱、地點、開始時間、結束時間等等。活動可能只會發生一次,也可能重複出現。與會者、提醒事項和擴充屬性會儲存在個別的資料表中。每個事件都包含一個 EVENT_ID,可參照「事件」表格中的 _ID
CalendarContract.Instances 這個表格包含 每次發生事件的開始和結束時間。此表格中的每一列 代表單一事件的發生次數如果是一次性活動,系統會提供 1:1 對應 事件例項對於週期性事件,系統會自動產生多個資料列,對應該事件的多個發生情形。
CalendarContract.Attendees 這個資料表會儲存活動與會者 (嘉賓) 的資訊。每一列都代表 活動。這項資訊會指定邀請對象的類型,以及邀請對象對活動的出席回覆。
CalendarContract.Reminders 這個資料表會儲存警示/通知資料。每一列都代表一項事件的單一警示。一個 活動可以設定多則提醒。每個活動的提醒次數上限會在 MAX_REMINDERS 中指定,由擁有指定日曆的同步處理器設定。提醒時間會以事件前幾分鐘為單位指定,並提供方法來決定使用者收到的通知方式。

Calendar Provider API 的設計兼具靈活性與強大功能,同時,您也必須提供良好的使用者體驗,並保護行事曆及其資料的完整性。為此,請注意以下幾點:

  • 插入、更新及查看日曆活動。如要直接從日曆供應器插入、修改及讀取事件,您必須具備適當的權限。不過,如果您不打算建構完善的日曆應用程式或同步轉換介面,就無需要求這些權限。您可以改用 Android 日曆應用程式支援的意圖,將讀取和寫入作業交給該應用程式。當您使用意圖時,應用程式會將使用者傳送至「日曆」應用程式,以執行所需作業。 預先填入表單完成後,系統會將這些要求退回您的應用程式。 透過設計應用程式,以透過日曆執行一般作業, 為使用者提供一致且穩固的使用者介面這是 。詳情請參閱「日曆意圖」。
  • 同步處理轉接器:同步處理器會將使用者裝置上的日曆資料與其他伺服器或資料來源同步處理。在 CalendarContract.CalendarsCalendarContract.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 布林值,指出是否要顯示該日曆。A 罩杯 值 0 表示與這個日曆相關聯的活動不應 。如果值為 1,表示系統應顯示與此日曆相關聯的活動。這個值會影響 CalendarContract.Instances 資料表中資料列的產生方式。
SYNC_EVENTS 布林值,指出是否要同步處理日曆,並包含該日曆 事件。值為 0 表示不要同步這份日曆或在裝置上儲存其活動。值為 1 表示這個日曆的同步處理活動 並將事件儲存在裝置上

為所有作業納入帳戶類型

Calendars.ACCOUNT_NAME 上查詢時,您必須一併加上 Calendars.ACCOUNT_TYPE 選取物件這是因為系統會根據 ACCOUNT_NAMEACCOUNT_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」、ACCOUNT_TYPE「com.example」和OWNER_ACCOUNT「hera@example.com」的 Google 日曆。如要查看使用者的所有日曆 的使用者已檢視,而不只是使用者擁有的日曆,請略過 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,做為 Uri (withAppendedId()) 的附加 ID 或第一個選取項目。所選項目應以 "_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_LOCALACCOUNT_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",表示事件應持續 1 小時,而值為 "P2W" 則表示持續時間為 2 週。
ALL_DAY 如果值為 1,表示此活動會佔據一整天, 當地時區。如果值為 0,表示這是一項可能在一天中的任何時間開始和結束的常規事件。
RRULE 事件格式的重複規則。適用對象 例如:"FREQ=WEEKLY;COUNT=10;WKST=SU"。如需更多範例,請參閱這篇文章
RDATE 活動的週期日期。 您通常會將 RDATERRULE 搭配使用,定義一組重複出現的項目。如需更多討論內容,請參閱 RFC5545 規格
AVAILABILITY 如果這個活動顯示為忙碌或有空, 排定時間
GUESTS_CAN_MODIFY 邀請對像是否能修改活動。
GUESTS_CAN_INVITE_OTHERS 邀請對像是否能邀請其他邀請對象。
GUESTS_CAN_SEE_GUESTS 邀請對象是否可查看與會者名單。

新增事件

當應用程式插入新事件時,建議您使用 INSERT 意圖,如「使用意圖插入事件」一文所述。不過,您可以視需要直接插入事件。本節將說明如何執行這項操作。

以下是插入新事件的規則:

以下是插入事件的範例。這會在使用者介面中執行 為簡單起見實務上,插入和更新作業應該是 非同步執行緒,將動作移至背景執行緒。詳情請參閱 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);

刪除活動

如要刪除事件,您可以使用事件的 _ID 做為 URI 的附加 ID,也可以使用 標準選項若使用附加的 ID,則無法選取。 刪除有兩種版本:應用程式和同步處理轉接程式。一個 應用程式刪除功能會將 deleted 資料欄設為 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_TYPEKEY_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

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


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

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

您也可以透過 Events.CONTENT_URI。 如需使用此意圖的範例,請參閱「使用意圖編輯事件」。
編輯 <event_id> 指定的事件。 CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

您也可以透過 Events.CONTENT_URI。 如需使用此意圖的範例,請參閱「使用意圖插入事件」。
建立活動。 下表列出的任何額外內容。

下表列出日曆供應商支援的意圖額外項目:

意圖額外資料 說明
Events.TITLE 事件的名稱。
CalendarContract.EXTRA_EVENT_BEGIN_TIME 事件開始時間,以 Epoch 紀元時間起算的毫秒數為單位。
CalendarContract.EXTRA_EVENT_END_TIME 事件結束時間,以 Epoch 紀元時間起算的毫秒數。
CalendarContract.EXTRA_EVENT_ALL_DAY 布林值,表示活動為全天。值可以是 truefalse
Events.EVENT_LOCATION 活動的地點。
Events.DESCRIPTION 活動說明。
Intent.EXTRA_EMAIL 列出邀請對象的電子郵件地址 (以半形逗號分隔)。
Events.RRULE 事件的重複規則。
Events.ACCESS_LEVEL 活動是否為私人或公開活動。
Events.AVAILABILITY 這項活動是否會計入忙碌時間,或是可預約的空閒時間。

以下章節說明如何使用這些意圖。

使用意圖插入事件

使用 INSERT 意圖可讓應用程式將活動插入作業交給日曆本身。使用這個方法時,應用程式甚至不需要在資訊清單檔案中加入 WRITE_CALENDAR 權限。

當使用者執行採用這種做法的應用程式時,應用程式會將使用者傳送至日曆,以便完成新增活動。INSERT 意圖會使用額外欄位 在表單中預先填入 Google 日曆的活動詳細資料。使用者可以 可以取消活動、視需要編輯表單,或將活動儲存至他們的帳戶 日曆。

以下是程式碼片段,可排定在 2012 年 1 月 19 日上午 7 點 30 分至 8 點 30 分執行的事件。請注意以下關於此程式碼片段的事項:

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_NAMEACCOUNT_TYPE 做為查詢參數。
  • 同步處理器的寫入權限比應用程式或小工具更廣泛,舉例來說,應用程式只能修改日曆的部分特徵,例如名稱、顯示名稱、瀏覽權限設定,以及日曆是否同步。相較之下,同步轉換介面不只能存取這些資料欄,還可存取許多其他資料欄 例如日曆顏色、時區、存取層級和位置等等 但同步轉換介面僅適用於 ACCOUNT_NAMEACCOUNT_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();
 }