Calendar Provider – Übersicht

Der Kalenderanbieter ist ein Repository für die Kalendertermine eines Nutzers. Mit der Kalenderanbieter API können Sie Vorgänge für Kalender, Termine, Teilnehmer, Erinnerungen usw. abfragen, einfügen, aktualisieren und löschen.

Die Calender Provider API kann von Anwendungen und Synchronisierungsadaptern verwendet werden. Die Regeln variieren je nach Art des Programms, das die Aufrufe durchführt. In diesem Dokument geht es hauptsächlich um die Verwendung der Calender Provider API als Anwendung. Weitere Informationen zu den Unterschieden zwischen Synchronisierungsadaptern finden Sie unter Synchronisierungsadapter.

Normalerweise muss das Manifest einer App zum Lesen oder Schreiben von Kalenderdaten die erforderlichen Berechtigungen enthalten, wie unter Nutzerberechtigungen beschrieben. Um gängige Vorgänge einfacher auszuführen, bietet der Kalenderanbieter eine Reihe von Intents, wie unter Kalender-Intents beschrieben. Über diese Intents werden Nutzer zur Kalenderanwendung weitergeleitet, um Termine einzufügen, anzusehen und zu bearbeiten. Der Nutzer interagiert mit der Kalenderanwendung und kehrt dann zur ursprünglichen Anwendung zurück. Daher muss Ihre Anwendung keine Berechtigungen anfordern oder eine Benutzeroberfläche zum Anzeigen oder Erstellen von Ereignissen bereitstellen.

Grundlegende Informationen

Contentanbieter speichern Daten und machen sie für Anwendungen zugänglich. Die von der Android-Plattform angebotenen Inhaltsanbieter (einschließlich des Kalenderanbieters) stellen Daten in der Regel in Form einer Reihe von Tabellen auf der Grundlage eines relationalen Datenbankmodells bereit. Dabei ist jede Zeile ein Datensatz und jede Spalte enthält Daten eines bestimmten Typs und einer bestimmten Bedeutung. Über die Calender Provider API erhalten Anwendungen und Synchronisierungsadapter Lese-/Schreibzugriff auf die Datenbanktabellen mit den Kalenderdaten eines Nutzers.

Jeder Contentanbieter stellt einen öffentlichen URI (verpackt als Uri-Objekt) bereit, der seinen Datensatz eindeutig identifiziert. Ein Contentanbieter, der mehrere Datasets (mehrere Tabellen) steuert, stellt für jeden einen separaten URI zur Verfügung. Alle URIs für Anbieter beginnen mit dem String „content://“. Dadurch wird angegeben, dass die Daten von einem Contentanbieter kontrolliert werden. Der Kalenderanbieter definiert Konstanten für die URIs für jede seiner Klassen (Tabellen). Diese URIs haben das Format <class>.CONTENT_URI. Beispiel: Events.CONTENT_URI.

Abbildung 1 zeigt eine grafische Darstellung des Datenmodells des Kalenderanbieters. Es werden die Haupttabellen und die Felder angezeigt, die sie miteinander verknüpfen.

Datenmodell des Kalenderanbieters

Abbildung 1: Datenmodell des Kalenderanbieters.

Ein Nutzer kann mehrere Kalender haben und verschiedene Kalender können mit verschiedenen Arten von Konten verknüpft sein (Google Kalender, Exchange usw.).

CalendarContract definiert das Datenmodell von kalender- und terminbezogenen Informationen. Diese Daten werden in verschiedenen Tabellen gespeichert, die unten aufgeführt sind.

Tabelle (Klasse) Beschreibung

CalendarContract.Calendars

Diese Tabelle enthält kalenderspezifische Informationen. Jede Zeile in dieser Tabelle enthält die Details für einen einzelnen Kalender, z. B. Name, Farbe, Synchronisierungsinformationen usw.
CalendarContract.Events Diese Tabelle enthält die ereignisspezifischen Informationen. Jede Zeile in dieser Tabelle enthält die Informationen für ein einzelnes Ereignis, z. B. Titel, Ort, Beginn und Ende der Veranstaltung. Das Ereignis kann einmalig oder mehrmals auftreten. Gäste, Erinnerungen und erweiterte Properties werden in separaten Tabellen gespeichert. Jedes Element hat ein EVENT_ID, das auf das _ID in der Ereignistabelle verweist.
CalendarContract.Instances Diese Tabelle enthält die Start- und Endzeit für jedes Auftreten eines Ereignisses. Jede Zeile in dieser Tabelle steht für ein einzelnes Ereignisvorkommen. Bei einmaligen Ereignissen werden die Instanzen 1:1 den Ereignissen zugeordnet. Bei wiederkehrenden Ereignissen werden automatisch mehrere Zeilen generiert, die mehreren Vorkommen dieses Ereignisses entsprechen.
CalendarContract.Attendees Diese Tabelle enthält die Informationen zum Ereignisteilnehmer (Gast). Jede Zeile steht für einen einzelnen Gast eines Ereignisses. Sie gibt den Typ des Gasts und seine Teilnahmeantwort auf den Termin an.
CalendarContract.Reminders Diese Tabelle enthält die Benachrichtigungsdaten. Jede Zeile steht für eine einzelne Benachrichtigung für ein Ereignis. Ein Termin kann mehrere Erinnerungen haben. Die maximale Anzahl von Erinnerungen pro Termin wird in MAX_REMINDERS angegeben. Sie wird vom Synchronisierungsadapter festgelegt, der Inhaber des jeweiligen Kalenders ist. Erinnerungen werden in Minuten vor dem Termin angegeben und haben eine Methode, die bestimmt, wie der Nutzer benachrichtigt wird.

Die Kalenderanbieter API ist flexibel und leistungsstark. Gleichzeitig ist es wichtig, eine gute Endnutzererfahrung zu bieten und die Integrität des Kalenders und seiner Daten zu schützen. Beachten Sie deshalb bei der Verwendung der API Folgendes:

  • Kalendertermine einfügen, aktualisieren und ansehen Wenn Sie Termine direkt vom Kalenderanbieter einfügen, ändern und lesen möchten, benötigen Sie die entsprechenden Berechtigungen. Wenn Sie jedoch keine vollwertige Kalenderanwendung oder keinen vollwertigen Synchronisierungsadapter erstellen, müssen diese Berechtigungen nicht angefordert werden. Sie können stattdessen von der Kalenderanwendung von Android unterstützte Intents verwenden, um Lese- und Schreibvorgänge an diese Anwendung zu übergeben. Wenn Sie die Intents verwenden, sendet Ihre Anwendung Nutzer an die Kalenderanwendung, um den gewünschten Vorgang in einem vorausgefüllten Formular auszuführen. Anschließend werden sie an Ihre Anwendung zurückgegeben. Wenn Sie Ihre Anwendung so gestalten, dass gängige Vorgänge über den Kalender ausgeführt werden, bieten Sie den Nutzern eine einheitliche, robuste Benutzeroberfläche. Dies ist der empfohlene Ansatz. Weitere Informationen finden Sie unter Kalender-Intents.
  • Adapter synchronisieren: Ein Synchronisierungsadapter synchronisiert die Kalenderdaten auf dem Gerät eines Nutzers mit einem anderen Server oder einer anderen Datenquelle. In den Tabellen CalendarContract.Calendars und CalendarContract.Events gibt es Spalten, die für die zu verwendenden Synchronisierungsadapter reserviert sind. Der Anbieter und die Anwendungen dürfen keine Änderungen daran vornehmen. Tatsächlich sind sie nur sichtbar, wenn sie über einen Synchronisierungsadapter aufgerufen werden. Weitere Informationen zu Synchronisierungsadaptern finden Sie unter Synchronisierungsadapter.

Nutzerberechtigungen

Zum Lesen von Kalenderdaten muss eine App die Berechtigung READ_CALENDAR in der Manifestdatei enthalten. Es muss die Berechtigung WRITE_CALENDAR zum Löschen, Einfügen oder Aktualisieren von Kalenderdaten enthalten:

<?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>

Kalendertabelle

Die Tabelle CalendarContract.Calendars enthält Details zu einzelnen Kalendern. Die folgenden Kalenderspalten können sowohl von einer Anwendung als auch einem Synchronisierungsadapter beschrieben werden. Eine vollständige Liste der unterstützten Felder finden Sie in der Referenz zu CalendarContract.Calendars.

Konstante Beschreibung
NAME Der Name des Kalenders.
CALENDAR_DISPLAY_NAME Der Name dieses Kalenders, den der Nutzer sieht.
VISIBLE Ein boolescher Wert, der angibt, ob der Kalender zur Anzeige ausgewählt ist. Der Wert 0 bedeutet, dass mit diesem Kalender verknüpfte Termine nicht angezeigt werden sollen. Der Wert „1“ bedeutet, dass mit diesem Kalender verknüpfte Termine angezeigt werden sollen. Dieser Wert wirkt sich auf die Generierung von Zeilen in der Tabelle CalendarContract.Instances aus.
SYNC_EVENTS Ein boolescher Wert, der angibt, ob der Kalender synchronisiert und die Termine auf dem Gerät gespeichert werden sollen. Der Wert 0 bedeutet, dass dieser Kalender nicht synchronisiert und keine Termine auf dem Gerät gespeichert werden sollen. Der Wert „1“ bedeutet, dass Termine für diesen Kalender synchronisiert und die Termine auf dem Gerät gespeichert werden.

Kontotyp für alle Vorgänge einschließen

Wenn Sie eine Abfrage für einen Calendars.ACCOUNT_NAME ausführen, müssen Sie auch Calendars.ACCOUNT_TYPE in die Auswahl aufnehmen. Das liegt daran, dass ein bestimmtes Konto nur dann als eindeutig gilt, wenn sowohl seine ACCOUNT_NAME als auch seine ACCOUNT_TYPE festgelegt sind. ACCOUNT_TYPE ist der String für die Kontoauthentifizierungs-App, die bei der Registrierung des Kontos mit AccountManager verwendet wurde. Für Kalender, die nicht mit einem Gerätekonto verknüpft sind, gibt es auch einen speziellen Kontotyp mit dem Namen ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCAL-Konten werden nicht synchronisiert.

Kalender abfragen

Das folgende Beispiel zeigt, wie Sie die Kalender abrufen, die einem bestimmten Nutzer gehören. Der Einfachheit halber wird in diesem Beispiel der Abfragevorgang im Benutzeroberflächen-Thread („Hauptthread“) dargestellt. In der Praxis sollte dies in einem asynchronen Thread statt im Hauptthread erfolgen. Weitere Informationen finden Sie unter Loader. Wenn Sie Daten nicht nur lesen, sondern ändern möchten, finden Sie unter AsyncQueryHandler weitere Informationen.

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;

Im nächsten Teil des Beispiels erstellen Sie Ihre Abfrage. Die Auswahl gibt die Kriterien für die Abfrage an. In diesem Beispiel wird nach Kalendern gesucht, die die ACCOUNT_NAME "hera@example.com", die ACCOUNT_TYPE "com.example" und die OWNER_ACCOUNT "hera@example.com" enthalten. Wenn Sie alle Kalender sehen möchten, die ein Nutzer angesehen hat, und nicht nur Kalender, die ihm gehören, lassen Sie OWNER_ACCOUNT weg. Die Abfrage gibt ein Cursor-Objekt zurück, mit dem Sie die von der Datenbankabfrage zurückgegebene Ergebnismenge durchsuchen können. Weitere Informationen zur Verwendung von Abfragen bei Contentanbietern finden Sie unter Contentanbieter.

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);

Im nächsten Abschnitt wird mit dem Cursor durch die Ergebnisse geführt. Die zu Beginn des Beispiels eingerichteten Konstanten werden verwendet, um die Werte für jedes Feld zurückzugeben.

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...

   ...
}

Kalender ändern

Um einen Kalender zu aktualisieren, können Sie die _ID des Kalenders entweder als angehängte ID an den URI (withAppendedId()) oder als erstes Auswahlelement angeben. Die Auswahl sollte mit "_id=?" beginnen und der erste selectionArg sollte der _ID des Kalenders sein. Sie können Aktualisierungen auch vornehmen, indem Sie die ID im URI codieren. In diesem Beispiel wird der Anzeigename eines Kalenders nach dem Ansatz (withAppendedId()) geändert:

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);

Kalender einfügen

Kalender sind so konzipiert, dass sie hauptsächlich über einen Synchronisierungsadapter verwaltet werden, daher sollten Sie neue Kalender nur als Synchronisierungsadapter einfügen. In den meisten Fällen können Anwendungen nur oberflächliche Änderungen an Kalendern vornehmen, z. B. den Anzeigenamen ändern. Wenn eine Anwendung einen lokalen Kalender erstellen muss, kann sie das Einfügen des Kalenders als Synchronisierungsadapter mit einem ACCOUNT_TYPE von ACCOUNT_TYPE_LOCAL ausführen. ACCOUNT_TYPE_LOCAL ist ein spezieller Kontotyp für Kalender, die nicht mit einem Gerätekonto verknüpft sind. Kalender dieses Typs werden nicht mit einem Server synchronisiert. Eine Beschreibung von Synchronisierungsadaptern finden Sie unter Synchronisierungsadapter.

Ereignistabelle

Die Tabelle CalendarContract.Events enthält Details zu einzelnen Ereignissen. Zum Hinzufügen, Aktualisieren oder Löschen von Ereignissen muss eine Anwendung die Berechtigung WRITE_CALENDAR in der Manifestdatei enthalten.

Die folgenden Ereignisspalten können sowohl von einer Anwendung als auch von einem Synchronisierungsadapter beschreibt werden. Eine vollständige Liste der unterstützten Felder finden Sie in der Referenz zu CalendarContract.Events.

Konstante Beschreibung
CALENDAR_ID _ID des Kalenders, zu dem der Termin gehört
ORGANIZER E-Mail-Adresse des Organisators (Inhabers) des Termins.
TITLE Der Titel des Termins.
EVENT_LOCATION Ort, an dem das Ereignis stattfindet.
DESCRIPTION Die Beschreibung des Ereignisses.
DTSTART Die Zeit, zu der das Ereignis seit der Epoche in UTC-Millisekunden beginnt.
DTEND Die Uhrzeit, zu der das Ereignis seit der Epoche in UTC-Millisekunden endet.
EVENT_TIMEZONE Die Zeitzone für den Termin.
EVENT_END_TIMEZONE Die Zeitzone für das Ende des Termins.
DURATION Die Dauer des Ereignisses im RFC5545-Format. Ein Wert von "PT1H" gibt beispielsweise an, dass das Ereignis eine Stunde dauern soll, und der Wert "P2W" gibt eine Dauer von 2 Wochen an.
ALL_DAY Der Wert „1“ bedeutet, dass dieses Ereignis den gesamten Tag gemäß der lokalen Zeitzone einnimmt. Der Wert 0 bedeutet, dass es sich um ein reguläres Ereignis handelt, das zu jeder Zeit eines Tages beginnen und enden kann.
RRULE Die Wiederholungsregel für das Ereignisformat. Beispiel: "FREQ=WEEKLY;COUNT=10;WKST=SU". Weitere Beispiele finden Sie hier.
RDATE Die Wiederholungsdaten des Termins. Normalerweise verwenden Sie RDATE in Verbindung mit RRULE, um eine Reihe von sich wiederholenden Vorkommen zu definieren. Weitere Informationen finden Sie in der RFC5545-Spezifikation.
AVAILABILITY Gibt an, ob es sich bei diesem Ereignis um eine kostenlose Zeit oder um eine kostenlose Zeit handelt, die verschoben werden kann.
GUESTS_CAN_MODIFY Gibt an, ob Gäste den Termin bearbeiten können.
GUESTS_CAN_INVITE_OTHERS Gibt an, ob Gäste weitere Gäste einladen können.
GUESTS_CAN_SEE_GUESTS Gibt an, ob Gäste die Teilnehmerliste sehen können.

Hinzufügen von Terminen

Wenn Ihre Anwendung ein neues Ereignis einfügt, empfehlen wir die Verwendung eines INSERT-Intents, wie unter Intent zum Einfügen eines Ereignisses verwenden beschrieben. Bei Bedarf können Sie Ereignisse jedoch auch direkt einfügen. In diesem Abschnitt wird beschrieben, wie Sie dies tun.

Folgende Regeln gelten für das Einfügen eines neuen Ereignisses:

Hier ist ein Beispiel für das Einfügen eines Ereignisses. Dies wird der Einfachheit halber im UI-Thread durchgeführt. In der Praxis sollten Einfügungen und Aktualisierungen in einem asynchronen Thread erfolgen, um die Aktion in einen Hintergrundthread zu verschieben. Weitere Informationen finden Sie unter 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
//
//

Hinweis:In diesem Beispiel wird die Ereignis-ID nach dem Erstellen des Ereignisses erfasst. Dies ist die einfachste Methode, um eine Ereignis-ID zu erhalten. Sie benötigen die Termin-ID häufig, um andere Kalendervorgänge auszuführen, z. B. um Teilnehmer oder Erinnerungen zu einem Termin hinzuzufügen.

Updates

Wenn Nutzer in Ihrer App die Möglichkeit haben, Ereignisse zu bearbeiten, empfehlen wir die Verwendung eines EDIT-Intents, wie unter Intent zum Bearbeiten eines Ereignisses verwenden beschrieben. Bei Bedarf können Sie Termine aber auch direkt bearbeiten. Wenn Sie ein Ereignis aktualisieren möchten, können Sie die _ID des Ereignisses entweder als angehängte ID an den URI (withAppendedId()) oder als erstes Auswahlelement angeben. Die Auswahl sollte mit "_id=?" beginnen und der erste selectionArg sollte der _ID des Ereignisses sein. Sie können Aktualisierungen auch mit einer Auswahl ohne ID vornehmen. Hier ist ein Beispiel für die Aktualisierung eines Ereignisses. Der Titel des Termins wird mit der Methode withAppendedId() geändert:

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);

Termine löschen

Sie können ein Ereignis entweder anhand seiner _ID als angehängte ID im URI oder über die Standardauswahl löschen. Wenn Sie eine angehängte ID verwenden, ist nicht gleichzeitig eine Auswahl möglich. Es gibt zwei Versionen von Delete: als Anwendung und als Synchronisierungsadapter. Beim Löschen einer Anwendung wird die Spalte deleted auf 1 gesetzt. Dieses Flag, das dem Synchronisierungsadapter mitteilt, dass die Zeile gelöscht wurde und dass die Löschung an den Server weitergeleitet werden soll. Durch das Löschen eines Synchronisierungsadapters werden das Ereignis und alle zugehörigen Daten aus der Datenbank entfernt. Hier ist ein Beispiel für eine Anwendung, die ein Ereignis über ihre _ID löscht:

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);

Tabelle „Teilnehmer“

Jede Zeile der Tabelle CalendarContract.Attendees stellt einen einzelnen Gast oder Gast eines Termins dar. Durch Aufrufen von query() wird eine Liste der Teilnehmer des Termins mit dem angegebenen EVENT_ID zurückgegeben. Dieser EVENT_ID muss mit dem _ID eines bestimmten Ereignisses übereinstimmen.

In der folgenden Tabelle sind die beschreibbaren Felder aufgeführt. Wenn Sie einen neuen Gast einfügen, müssen Sie alle außer ATTENDEE_NAME einschließen.

Konstante Beschreibung
EVENT_ID Die ID des Ereignisses.
ATTENDEE_NAME Der Name des Teilnehmers.
ATTENDEE_EMAIL Die E-Mail-Adresse des Teilnehmers.
ATTENDEE_RELATIONSHIP

Die Beziehung des Teilnehmers zum Ereignis. Eine der folgenden Optionen:

ATTENDEE_TYPE

Der Teilnehmertyp. Eine der folgenden Optionen:

ATTENDEE_STATUS

Der Teilnahmestatus des Teilnehmers. Eine der folgenden Optionen:

Teilnehmer hinzufügen

In diesem Beispiel wird einem Termin ein einzelner Gast hinzugefügt. Beachten Sie, dass EVENT_ID erforderlich ist:

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);

Tabelle mit Erinnerungen

Jede Zeile der Tabelle CalendarContract.Reminders steht für eine einzelne Erinnerung für ein Ereignis. Beim Aufrufen von query() wird eine Liste von Erinnerungen für das Ereignis mit dem angegebenen EVENT_ID zurückgegeben.

In der folgenden Tabelle sind die beschreibbaren Felder für Erinnerungen aufgeführt. Alle müssen beim Einfügen einer neuen Erinnerung angegeben werden. Beachten Sie, dass Synchronisierungsadapter die unterstützten Erinnerungstypen in der Tabelle CalendarContract.Calendars angeben. Weitere Informationen finden Sie unter ALLOWED_REMINDERS.

Konstante Beschreibung
EVENT_ID Die ID des Ereignisses.
MINUTES Die Minuten vor dem Ereignis, in denen die Erinnerung ausgelöst werden soll.
METHOD

Die auf dem Server festgelegte Alarmmethode. Eine der folgenden Optionen:

Erinnerungen erstellen

In diesem Beispiel wird einem Termin eine Erinnerung hinzugefügt. Die Erinnerung wird 15 Minuten vor dem Termin ausgelöst.

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);

Instanztabelle

Die Tabelle CalendarContract.Instances enthält die Start- und Endzeit für das Auftreten eines Ereignisses. Jede Zeile in dieser Tabelle steht für ein einzelnes Ereignisvorkommen. Die Instanztabelle ist nicht beschreibbar und bietet nur eine Möglichkeit, Ereignisvorkommen abzufragen.

In der folgenden Tabelle sind einige Felder aufgeführt, die Sie für eine Instanz abfragen können. Die Zeitzone wird durch KEY_TIMEZONE_TYPE und KEY_TIMEZONE_INSTANCES definiert.

Konstante Beschreibung
BEGIN Die Startzeit der Instanz in UTC-Millisekunden.
END Die Endzeit der Instanz in UTC-Millisekunden.
END_DAY Der julianische Endtag der Instanz im Verhältnis zur Zeitzone des Kalenders.
END_MINUTE Die Endminute der Instanz, gemessen ab Mitternacht in der Zeitzone des Kalenders.
EVENT_ID Der _ID des Ereignisses für diese Instanz.
START_DAY Der julianische Starttag der Instanz im Verhältnis zur Zeitzone des Kalenders.
START_MINUTE Die Startminute der Instanz, gemessen ab Mitternacht, bezogen auf die Zeitzone des Kalenders.

Instanztabelle abfragen

Wenn Sie die Instanztabelle abfragen möchten, müssen Sie im URI eine Bereichszeit für die Abfrage angeben. In diesem Beispiel erhält CalendarContract.Instances über die Implementierung der CalendarContract.EventsColumns-Schnittstelle Zugriff auf das Feld TITLE. Mit anderen Worten: TITLE wird über eine Datenbankansicht zurückgegeben und nicht durch Abfragen der Rohtabelle 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()));
    }
 }

Kalender-Intents

Ihre Anwendung benötigt keine Berechtigungen zum Lesen und Schreiben von Kalenderdaten. Er kann stattdessen Intents verwenden, die von der Kalenderanwendung von Android unterstützt werden, um Lese- und Schreibvorgänge an diese Anwendung zu übergeben. In der folgenden Tabelle sind die vom Kalenderanbieter unterstützten Intents aufgeführt:

Aktion URI Beschreibung Extras

VIEW

content://com.android.calendar/time/<ms_since_epoch>

Sie können auch mit CalendarContract.CONTENT_URI auf den URI verweisen. Ein Beispiel für die Verwendung dieses Intents finden Sie unter Intents zum Ansehen von Kalenderdaten verwenden.
Den Kalender für die in <ms_since_epoch> angegebene Zeit öffnen. Keine.

VIEW

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

Sie können auch mit Events.CONTENT_URI auf den URI verweisen. Ein Beispiel für die Verwendung dieses Intents finden Sie unter Intents zum Ansehen von Kalenderdaten verwenden.
Sehen Sie sich das in <event_id> angegebene Ereignis an. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

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

Sie können auch mit Events.CONTENT_URI auf den URI verweisen. Ein Beispiel für die Verwendung dieses Intents finden Sie unter Intent zum Bearbeiten eines Ereignisses verwenden.
Bearbeite das in <event_id> angegebene Ereignis. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

Sie können auch mit Events.CONTENT_URI auf den URI verweisen. Ein Beispiel für die Verwendung dieses Intents finden Sie unter Mit einem Intent ein Ereignis einfügen.
Erstellen Sie einen Termin. Alle in der Tabelle unten aufgeführten Extras.

In der folgenden Tabelle sind die vom Kalenderanbieter unterstützten Intent-Extras aufgeführt:

Intent Extra Beschreibung
Events.TITLE Name des Ereignisses.
CalendarContract.EXTRA_EVENT_BEGIN_TIME Ereignisbeginn in Millisekunden seit der Epoche.
CalendarContract.EXTRA_EVENT_END_TIME Ereignisende in Millisekunden seit Epoche.
CalendarContract.EXTRA_EVENT_ALL_DAY Ein boolescher Wert, der angibt, dass ein Ereignis ganztägig ist. Der Wert kann true oder false sein.
Events.EVENT_LOCATION Ort des Ereignisses.
Events.DESCRIPTION Ereignisbeschreibung.
Intent.EXTRA_EMAIL E-Mail-Adressen der einzuladenden Personen als durch Kommas getrennte Liste.
Events.RRULE Die Wiederholungsregel für den Termin.
Events.ACCESS_LEVEL Gibt an, ob der Termin privat oder öffentlich ist.
Events.AVAILABILITY Ob es sich um einen kostenlosen Termin oder eine kostenlose Zeit handelt, die verschoben werden kann.

In den folgenden Abschnitten wird die Verwendung dieser Intents beschrieben.

Intent zum Einfügen eines Ereignisses verwenden

Mit dem Intent INSERT kann Ihre Anwendung die Aufgabe zum Einfügen eines Termins an den Kalender selbst übergeben. Bei diesem Ansatz muss für deine App nicht einmal die Berechtigung WRITE_CALENDAR in der Manifestdatei enthalten sein.

Wenn Nutzer eine Anwendung mit diesem Ansatz ausführen, sendet sie sie an den Kalender, um das Hinzufügen des Termins abzuschließen. Der Intent INSERT verwendet zusätzliche Felder, um ein Formular vorab mit den Details des Termins im Kalender auszufüllen. Nutzer können den Termin dann absagen, das Formular nach Bedarf bearbeiten oder den Termin in ihren Kalendern speichern.

Hier sehen Sie ein Code-Snippet, mit dem ein Ereignis für den 19. Januar 2012 geplant wird, das von 7:30 Uhr bis 8:30 Uhr stattfindet. Beachten Sie Folgendes zu diesem Code-Snippet:

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);

Intent zum Bearbeiten eines Ereignisses verwenden

Sie können Ereignisse direkt aktualisieren. Eine Anleitung dazu finden Sie unter Ereignisse aktualisieren. Wenn Sie jedoch den Intent EDIT verwenden, kann eine Anwendung, die nicht berechtigt ist, die Terminbearbeitung an die Kalenderanwendung zu übergeben. Wenn Nutzer mit der Bearbeitung ihres Termins in Google Kalender fertig sind, werden sie zur ursprünglichen Anwendung zurückgeleitet.

Hier ist ein Beispiel für einen Intent, mit dem für einen bestimmten Termin ein neuer Titel festgelegt wird und Nutzer den Termin im Kalender bearbeiten können.

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);

Intents zum Ansehen von Kalenderdaten verwenden

Kalenderanbieter bietet zwei verschiedene Möglichkeiten, den Intent VIEW zu verwenden:

  • So öffnen Sie den Kalender für ein bestimmtes Datum:
  • Um einen Termin anzusehen.

Das folgende Beispiel zeigt, wie Sie den Kalender für ein bestimmtes Datum öffnen:

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);

Das folgende Beispiel zeigt, wie Sie einen Termin zur Ansicht öffnen:

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);

Adapter synchronisieren

Es gibt nur geringfügige Unterschiede darin, wie eine Anwendung und ein Synchronisierungsadapter auf den Kalenderanbieter zugreifen:

  • Ein Synchronisierungsadapter muss angeben, dass er ein Synchronisierungsadapter ist, indem Sie CALLER_IS_SYNCADAPTER auf true setzen.
  • Ein Synchronisierungsadapter muss ein ACCOUNT_NAME und ein ACCOUNT_TYPE als Abfrageparameter im URI angeben.
  • Ein Synchronisierungsadapter hat Schreibzugriff auf mehr Spalten als eine Anwendung oder ein Widget. Beispielsweise kann eine Anwendung nur wenige Eigenschaften eines Kalenders ändern, z. B. den Namen, den Anzeigenamen, die Sichtbarkeitseinstellung und ob der Kalender synchronisiert wird. Im Vergleich dazu kann ein Synchronisierungsadapter nicht nur auf diese Spalten zugreifen, sondern auch auf viele andere, z. B. auf Kalenderfarbe, Zeitzone, Zugriffsebene, Standort usw. Ein Synchronisierungsadapter ist jedoch auf die angegebenen ACCOUNT_NAME und ACCOUNT_TYPE beschränkt.

Hier ist eine Hilfsmethode, mit der Sie einen URI zur Verwendung mit einem Synchronisierungsadapter zurückgeben können:

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();
 }