Omówienie dostawcy kalendarza

Dostawca kalendarza to repozytorium wydarzeń w kalendarzu użytkownika. Interfejs Calendar Provider API umożliwia wykonywanie operacji wyszukiwania, wstawiania, aktualizowania i usuwania dotyczących kalendarzy, wydarzeń, uczestników, przypomnień itp.

Interfejsu Calendar Provider API mogą używać aplikacje i adaptery synchronizacji. Zasady różnią się w zależności od typu programu. Ten dokument koncentruje się głównie na korzystaniu z interfejsu Calendar Provider API jako aplikacji. Informacje o tym, czym różnią się adaptery synchronizacji, znajdziesz w artykule Adaptery synchronizacji.

Aby odczytywać lub zapisywać dane z kalendarza, aplikacja musi mieć w pliku manifestu odpowiednie uprawnienia, opisane w Uprawnieniach użytkownika. Aby ułatwić wykonywanie typowych operacji, dostawca kalendarza udostępnia zestaw intencji opisanych w artykule Intencje dotyczące kalendarza. Te intencje przenoszą użytkowników do aplikacji Kalendarz, gdzie mogą wstawiać, wyświetlać i edytować wydarzenia. Użytkownik wchodzi w interakcję z aplikacją Kalendarz, a potem wraca do pierwotnej aplikacji. Dlatego aplikacja nie musi prosić o uprawnienia ani udostępniać interfejsu użytkownika do wyświetlania lub tworzenia zdarzeń.

Podstawy

Dostawcy treści przechowują dane i umieszczają je w dostępnym dla aplikacji miejscu. Dostawcy treści oferowani przez platformę Androida (w tym dostawcę Kalendarza) zwykle ujawniają dane w postaci zbioru tabel opartego na relacyjnym modelu bazy danych, w którym każdy wiersz to rekord, a każda kolumna zawiera dane określonego typu i znaczenia. Dzięki interfejsowi Calendar Provider API aplikacje i adaptery synchronizacji mogą uzyskać uprawnienia do odczytu/zapisu w tabelach bazy danych, które zawierają dane z kalendarza użytkownika.

Każdy dostawca treści udostępnia publiczny identyfikator URI (opakowany jako obiekt Uri), który jednoznacznie identyfikuje jego zbiór danych. Dostawca treści, który zarządza wieloma zbiorami danych (wiele tabel), udostępnia dla każdego z nich osobny adres URI. Wszystkie identyfikatory URI dostawców zaczynają się od ciągu „content://”. Wskazuje, że dane są kontrolowane przez dostawcę treści. Dostawca kalendarza definiuje stałe dla URI dla każdej ze swoich klas (tabel). Te URI mają format <class>.CONTENT_URI. Przykład: Events.CONTENT_URI.

Rysunek 1 przedstawia graficzne przedstawienie modelu danych dostawcy kalendarza. Pokazuje ona główne tabele i pola, które je ze sobą łączą.

Model danych dostawcy kalendarza

Rysunek 1. Model danych dostawcy kalendarza.

Użytkownik może mieć wiele kalendarzy i różne kalendarze mogą być powiązane z różnymi typami kont (Kalendarz Google, Exchange itd.).

CalendarContract definiuje model danych kalendarza i informacji związanych z wydarzeniami. Te dane są przechowywane w kilku tabelach wymienionych poniżej.

Tabela (klasa) Opis

CalendarContract.Calendars

Ta tabela zawiera informacje dotyczące kalendarza. Każdy wiersz w tej tabeli zawiera informacje o poszczególnych kalendarzach, takie jak nazwa, kolor, informacje o synchronizacji itp.
CalendarContract.Events Ta tabela zawiera informacje o konkretnym zdarzeniu. Każdy wiersz w tej tabeli zawiera informacje o pojedynczym zdarzeniu, np. tytuł, lokalizację, godzinę rozpoczęcia i zakończenia itp. Zdarzenie może wystąpić raz lub wielokrotnie. Uczestnicy, przypomnienia i rozszerzone właściwości są przechowywane w osobnych tabelach. Każdy z nich ma kolumnę EVENT_ID, która odwołuje się do kolumny _ID w tabeli Zdarzenia.
CalendarContract.Instances Ta tabela zawiera czas rozpoczęcia i zakończenia każdego wystąpienia zdarzenia. Każdy wiersz w tej tabeli odpowiada pojedynczemu wystąpieniu zdarzenia. W przypadku jednorazowych zdarzeń występuje mapowanie 1:1 wydarzeń na wystąpienia. W przypadku zdarzeń cyklicznych automatycznie generowane są liczne wiersze odpowiadające wielu wystąpieniom danego zdarzenia.
CalendarContract.Attendees Ta tabela zawiera informacje o uczestniku (gościu) wydarzenia. Każdy wiersz odpowiada jednemu gościowi wydarzenia. Określa typ gościa i jego odpowiedź na zaproszenie na wydarzenie.
CalendarContract.Reminders Ta tabela zawiera dane o alertach i powiadomieniach. Każdy wiersz odpowiada jednemu alarmowi dotyczącemu zdarzenia. Wydarzenie może mieć wiele przypomnień. Maksymalna liczba przypomnień na wydarzenie jest określona w parametrye MAX_REMINDERS, który jest ustawiany przez adapter synchronizacji będący właścicielem danego kalendarza. Przypomnienia są określane w minutach przed wydarzeniem i mają metodę, która określa, jak użytkownik zostanie o nim powiadomiony.

Interfejs Calendar Provider API jest elastyczny i wydajny. Jednocześnie ważne jest zapewnienie wygody użytkownikom oraz ochrona integralności kalendarza i jego danych. Dlatego przy korzystaniu z interfejsu API warto pamiętać o tych kwestiach:

  • Wstawianie, aktualizowanie i wyświetlanie wydarzeń w kalendarzu Aby bezpośrednio wstawiać, modyfikować i odczytywać wydarzenia z poziomu dostawcy kalendarza, potrzebujesz odpowiednich uprawnień. Jeśli jednak nie tworzysz pełnej aplikacji kalendarza ani adaptera synchronizacji, nie musisz prosić o te uprawnienia. Zamiast tego możesz użyć intencji obsługiwanych przez aplikację Kalendarz na Androidzie, aby przekazać tej aplikacji operacje odczytu i zapisu. Gdy używasz intencji, Twoja aplikacja wysyła użytkowników do aplikacji Kalendarz, aby wykonać odpowiednią operację w wypełnionym formularzu. Gdy to zrobią, są zwracane do Twojej aplikacji. Projektując aplikację tak, aby wykonywała typowe operacje w Kalendarzu, zapewniasz użytkownikom spójny i wydajny interfejs. To jest zalecane podejście. Więcej informacji znajdziesz w artykule Intencje dotyczące Kalendarza.
  • Synchroniz adaptery. Adapter synchronizacji synchronizuje dane kalendarza na urządzeniu użytkownika z innym serwerem lub źródłem danych. W tabelach CalendarContract.Calendars i CalendarContract.Events znajdują się kolumny zarezerwowane dla adapterów synchronizacji. Dostawca i aplikacje nie powinny ich modyfikować. W zasadzie są one niewidoczne, dopóki nie zostaną otwarte jako adapter synchronizacji. Więcej informacji o adapterach synchronizacji znajdziesz w artykule Adaptery synchronizacji.

Uprawnienia użytkowników

Aby odczytywać dane z kalendarza, aplikacja musi zawierać w pliku manifestu uprawnienie READ_CALENDAR. Musi ono zawierać uprawnienie WRITE_CALENDAR do usuwania, wstawiania i aktualizowania danych kalendarza:

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

Tabela kalendarzy

Tabela CalendarContract.Calendars zawiera szczegółowe informacje o poszczególnych kalendarzach. W tych kolumnach Kalendarza można zapisywać dane zarówno w aplikacji, jak i w adapterze synchronizacji. Pełną listę obsługiwanych pól znajdziesz w dokumentacji CalendarContract.Calendars.

Stała Opis
NAME Nazwa kalendarza.
CALENDAR_DISPLAY_NAME Nazwa tego kalendarza wyświetlana użytkownikowi.
VISIBLE Wartość logiczna wskazująca, czy kalendarz ma być wyświetlany. Wartość 0 oznacza, że wydarzenia powiązane z tym kalendarzem nie powinny być wyświetlane. Wartość 1 oznacza, że zdarzenia powiązane z tym kalendarzem powinny być wyświetlane. Ta wartość wpływa na generowanie wierszy w tabeli CalendarContract.Instances.
SYNC_EVENTS Wartość logiczna wskazująca, czy kalendarz ma być synchronizowany i czy jego wydarzenia mają być przechowywane na urządzeniu. Wartość 0 oznacza, że kalendarz nie jest zsynchronizowany ani nie przechowuje jego wydarzeń na urządzeniu. Wartość 1 oznacza, że wydarzenia z tego kalendarza są synchronizowane i przechowywane na urządzeniu.

Uwzględnij typ konta dla wszystkich operacji

Jeśli wysyłasz zapytanie dotyczące Calendars.ACCOUNT_NAME, musisz też uwzględnić w wybranych elementach Calendars.ACCOUNT_TYPE. Dzieje się tak, ponieważ dane konto jest uważane za unikalne tylko wtedy, gdy ma zarówno ACCOUNT_NAME, jak i ACCOUNT_TYPE. Wartość ACCOUNT_TYPE to ciąg znaków odpowiadający uwierzytelniającemu kontu, który został użyty podczas rejestracji konta w usłudze AccountManager. Istnieje też specjalny typ konta o nazwie ACCOUNT_TYPE_LOCAL, który służy do obsługi kalendarzy nieprzypisanych do konta urządzenia. Konta ACCOUNT_TYPE_LOCAL nie są synchronizowane.

Wysyłanie zapytania do kalendarza

Oto przykład pokazujący, jak pobrać kalendarze należące do konkretnego użytkownika. W tym przykładzie operacja zapytania jest dla uproszczenia wyświetlana w wątku interfejsu użytkownika („główny wątek”). W praktyce należy to zrobić w wątku asynchronicznym, a nie w wątku głównym. Więcej informacji znajdziesz w artykule Ładowarki. Jeśli nie tylko odczytujecie dane, ale je modyfikujecie, zapoznajcie się z artykułem 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;

W następnej części przykładu tworzysz zapytanie. Wybór określa kryteria zapytania. W tym przykładzie zapytanie wyszukuje kalendarze, które mają ACCOUNT_NAME „hera@example.com”, ACCOUNT_TYPE „com.example” i OWNER_ACCOUNT „hera@example.com”. Jeśli chcesz zobaczyć wszystkie kalendarze, które użytkownik wyświetlił, a nie tylko te, które mu należą, pomiń parametr OWNER_ACCOUNT. Zapytanie zwraca obiekt Cursor, którego możesz użyć do przejścia przez zbiór wyników zwrócony przez zapytanie do bazy danych. Więcej informacji o używaniu zapytań w dostawcy treści znajdziesz w artykule Dostawcy treści.

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

W następnej sekcji możesz przeglądać zestaw wyników za pomocą kursora. Do zwracania wartości poszczególnych pól używa stałych zdefiniowanych na początku przykładu.

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

   ...
}

Modyfikowanie kalendarza

Aby przeprowadzić aktualizację kalendarza, możesz podać _ID kalendarza jako identyfikator dołączony do identyfikatora URI (withAppendedId()) lub jako pierwszy element wyboru. Wybór powinien zaczynać się od "_id=?", a pierwszy element w listach selectionArg powinien być _ID z kalendarza. Możesz też wprowadzać zmiany, kodując identyfikator w identyfikatorze URI. W tym przykładzie zmieniamy nazwę wyświetlaną kalendarza za pomocą właściwości 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);

Wstawianie kalendarza

Kalendarzami zarządza się głównie za pomocą adaptera synchronizacji, dlatego nowe kalendarze należy dodawać tylko jako adaptery synchronizacji. W większości przypadków aplikacje mogą wprowadzać tylko powierzchowne zmiany w kalendarzach, takie jak zmiana wyświetlanej nazwy. Jeśli aplikacja musi utworzyć lokalny kalendarz, może to zrobić, wykonując wstawienie kalendarza jako adapter synchronizacji, używając ACCOUNT_TYPE ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCALto specjalny typ konta dla kalendarzy, które nie są powiązane z kontem urządzenia. Kalendarze tego typu nie są synchronizowane z serwerem. Opis adapterów synchronizacji znajdziesz w artykule Adaptery synchronizacji.

Tabela zdarzeń

Tabela CalendarContract.Events zawiera szczegóły poszczególnych zdarzeń. Aby dodawać, aktualizować i usuwać zdarzenia, aplikacja musi mieć w pliku manifestu uprawnienia WRITE_CALENDAR.

Poniższe kolumny Zdarzenia mogą zapisywać zarówno aplikację, jak i adapter synchronizacji. Pełną listę obsługiwanych pól znajdziesz w dokumentacji CalendarContract.Events.

Stała Opis
CALENDAR_ID _ID kalendarza, do którego należy wydarzenie.
ORGANIZER Adres e-mail organizatora (właściciela) wydarzenia.
TITLE Tytuł wydarzenia.
EVENT_LOCATION Miejsce, w którym odbywa się wydarzenie.
DESCRIPTION Opis zdarzenia.
DTSTART Czas rozpoczęcia zdarzenia w milisekundach UTC od początku epoki.
DTEND Czas zakończenia zdarzenia w milisekundach UTC od początku epoki.
EVENT_TIMEZONE Strefa czasowa wydarzenia.
EVENT_END_TIMEZONE Strefa czasowa godziny zakończenia wydarzenia.
DURATION Czas trwania zdarzenia w formacie RFC 5545. Na przykład wartość "PT1H" wskazuje, że wydarzenie powinno trwać godzinę, a wartość "P2W" – czas trwania 2 tygodnie.
ALL_DAY Wartość 1 oznacza, że to zdarzenie trwa cały dzień zgodnie ze strefą czasową lokalną. Wartość 0 oznacza zwykłe zdarzenie, które może się rozpocząć i zakończyć o dowolnej porze dnia.
RRULE Reguła powtarzania formatu zdarzenia. Na przykład: "FREQ=WEEKLY;COUNT=10;WKST=SU". Więcej przykładów znajdziesz tutaj.
RDATE Daty powtarzania wydarzenia. Zazwyczaj używasz instrukcji RDATE w połączeniu z instrukcją RRULE, aby zdefiniować zbiorczy zestaw powtarzających się wystąpień. Więcej informacji znajdziesz w specyfikacji RFC5545.
AVAILABILITY Czy to wydarzenie zalicza się do czasu dużego ruchu lub jest wolnym czasem, które można zaplanować.
GUESTS_CAN_MODIFY Określa, czy goście mogą modyfikować wydarzenie.
GUESTS_CAN_INVITE_OTHERS czy goście mogą zapraszać innych gości.
GUESTS_CAN_SEE_GUESTS Określa, czy goście mogą wyświetlać listę uczestników.

Dodawanie wydarzeń

Gdy aplikacja wstawia nowe zdarzenie, zalecamy użycie intencji INSERT w sposób opisany w artykule Korzystanie z intencji do wstawiania zdarzenia. Jeśli jednak chcesz, możesz wstawiać zdarzenia bezpośrednio. W tej sekcji dowiesz się, jak to zrobić.

Oto reguły dotyczące wstawiania nowego zdarzenia:

Oto przykład wstawiania zdarzenia. Dla uproszczenia jest to wykonywane w wątku UI. W praktyce wstawianie i aktualizowanie powinno odbywać się w wątku asynchronicznym, aby przekazać działanie do wątku tła. Więcej informacji: 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
//
//

Uwaga: w tym przykładzie pokazano, jak po utworzeniu zdarzenia jest pobierany jego identyfikator. Jest to najłatwiejszy sposób uzyskania identyfikatora zdarzenia. Często potrzebujesz identyfikatora wydarzenia, aby wykonywać inne operacje w kalendarzu, na przykład dodawać uczestników lub przypomnienia.

Aktualizacje

Jeśli aplikacja ma umożliwiać użytkownikowi edytowanie zdarzenia, zalecamy użycie EDITintencji, jak opisano w artykule Używanie intencji do edytowania zdarzenia. W razie potrzeby możesz jednak edytować zdarzenia bezpośrednio. Aby zaktualizować zdarzenie, możesz podać _ID zdarzenia jako dołączony identyfikator do adresu Uri (withAppendedId()) lub jako pierwszy element wyboru. Wybór powinien zaczynać się od "_id=?", a pierwszy selectionArg powinien być _ID zdarzenia. Możesz też wprowadzać zmiany za pomocą selekcji bez identyfikatora. Oto przykład aktualizacji zdarzenia. Tytuł wydarzenia zmienia się za pomocą metody 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);

Usuń wydarzenia

Zdarzenie możesz usunąć, korzystając z jego identyfikatora _ID jako dołączonego identyfikatora URI lub za pomocą standardowego wyboru. Jeśli używasz dołączonego identyfikatora, nie możesz też dokonać wyboru. Istnieją 2 wersje funkcji usuwania: jako aplikacja i jako adapter synchronizacji. Usunięcie aplikacji powoduje ustawienie kolumny deleted na wartość 1. Ten parametr informuje adapter synchronizacji, że wiersz został usunięty i że to usunięcie powinno zostać przeniesione na serwer. Usunięcie za pomocą adaptera synchronizacji powoduje usunięcie zdarzenia z bazy danych wraz ze wszystkimi powiązanymi z nim danymi. Oto przykład aplikacji usuwającej zdarzenie za pomocą funkcji _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);

Tabela uczestników

Każdy wiersz w tabeli CalendarContract.Attendees odpowiada jednemu uczestnikowi lub gościowi wydarzenia. Wywołanie query() zwraca listę uczestników wydarzenia z danym EVENT_ID. Ten element EVENT_IDmusi być zgodny z elementem _ID konkretnego zdarzenia.

W tabeli poniżej znajdziesz pola, które można edytować. Podczas wstawiania nowego uczestnika musisz uwzględnić wszystkich, z wyjątkiem ATTENDEE_NAME.

Stała Opis
EVENT_ID Identyfikator wydarzenia.
ATTENDEE_NAME Imię i nazwisko uczestnika.
ATTENDEE_EMAIL Adres e-mail uczestnika.
ATTENDEE_RELATIONSHIP

Relacja między uczestnikiem a wydarzeniem. Jedna z tych możliwości:

ATTENDEE_TYPE

Typ uczestnika. Jedno z tych:

ATTENDEE_STATUS

Stan obecności uczestnika. Jedno z tych:

Dodaj uczestników

Oto przykład dodawania jednego uczestnika do wydarzenia. Pamiętaj, że EVENT_ID jest wymagane:

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

Tabela przypomnień

Każdy wiersz w tabeli CalendarContract.Reminders odpowiada jednemu przypomnieniu o zdarzeniu. Wywołanie query() zwraca listę przypomnień dla wydarzenia o podanym EVENT_ID.

W tabeli poniżej znajdziesz pola, które można modyfikować w przypadku przypomnień. Wszystkie te informacje muszą być uwzględnione podczas dodawania nowego przypomnienia. Pamiętaj, że w tabeli CalendarContract.Calendars adaptery synchronizacji podają typy obsługiwanych przypomnień. Więcej informacji znajdziesz na stronie ALLOWED_REMINDERS.

Stała Opis
EVENT_ID Identyfikator zdarzenia.
MINUTES Liczba minut przed wydarzeniem, w którym ma się uruchomić przypomnienie.
METHOD

Metoda alarmu skonfigurowana na serwerze. Jedno z tych:

Dodawaj przypomnienia

W tym przykładzie dodaję przypomnienie do wydarzenia. Przypomnienie zostanie wysłane 15 minut przed wydarzeniem.

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

Tabela instancji

Tabela CalendarContract.Instances zawiera czas rozpoczęcia i zakończenia wystąpienia zdarzenia. Każdy wiersz w tej tabeli odpowiada 1 wystąpieniu zdarzenia. W tabeli instancji nie można zapisywać danych. Tabela ta służy tylko do uzyskiwania informacji o wystąpieniach zdarzeń.

W tabeli poniżej znajdziesz listę niektórych pól, których możesz używać w zapytaniach dotyczących instancji. Pamiętaj, że strefa czasowa jest określana przez KEY_TIMEZONE_TYPE i KEY_TIMEZONE_INSTANCES.

Stała Opis
BEGIN Czas rozpoczęcia instancji w milisekundach UTC.
END Czas zakończenia instancji w milisekundach UTC.
END_DAY Dzień juliański zakończenia instancji w zględzie strefy czasowej kalendarza.
END_MINUTE Minuta zakończenia wystąpienia liczona od północy w strefie czasowej Kalendarza.
EVENT_ID _ID zdarzenia w danym przypadku.
START_DAY Dzień juliański rozpoczęcia instancji w stosunku do strefy czasowej Kalendarza.
START_MINUTE Minuta rozpoczęcia instancji mierzona od północy w strefie czasowej Kalendarza.

Wykonywanie zapytań do tabeli instancji

Aby wysłać zapytanie do tabeli instancji, musisz w identyfikatorze URI podać zakres czasu zapytania. W tym przykładzie CalendarContract.Instances uzyskuje dostęp do pola TITLE przez implementację interfejsu CalendarContract.EventsColumns. Innymi słowy, TITLE jest zwracany przez widok bazy danych, a nie przez zapytanie do nieprzetworzonej tabeli 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()));
    }
 }

Intencje dotyczące kalendarza

Aplikacja nie musi mieć uprawnień do odczytywania i zapisywania danych z kalendarza. Zamiast tego może używać intencji obsługiwanych przez aplikację Kalendarz na Androidzie, aby przekazać tej aplikacji operacje odczytu i zapisu. W tabeli poniżej znajdziesz listę intencji obsługiwanych przez dostawcę kalendarza:

Działanie URI Opis Dodatkowe treści

VIEW

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

Możesz też użyć URI z CalendarContract.CONTENT_URI. Przykład użycia tego zamiaru znajdziesz w artykule Wyświetlanie danych z kalendarza za pomocą zamiarów.
Otwórz kalendarz na czas określony przez <ms_since_epoch>. Brak.

VIEW

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

Możesz też użyć URI z Events.CONTENT_URI. Przykład wykorzystania tej intencji znajdziesz w artykule o używaniu intencji do wyświetlania danych z kalendarza (w języku angielskim).
Wyświetl zdarzenie określone przez <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

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

Możesz też odwołać się do identyfikatora URI za pomocą polecenia Events.CONTENT_URI. Przykład użycia tej intencji znajdziesz w sekcji Używanie intencji do edytowania zdarzenia.
Edytuj zdarzenie określone przez funkcję <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

Możesz też użyć URI z Events.CONTENT_URI. Przykład użycia tej intencji znajdziesz w artykule Używanie intencji do wstawiania zdarzeń.
Utwórz wydarzenie. wszystkie dodatkowe usługi wymienione w tabeli poniżej.

W tej tabeli znajdziesz dodatkowe dodatki do intencji obsługiwane przez dostawcę kalendarza:

Dodatkowa intencja Opis
Events.TITLE Nazwa zdarzenia.
CalendarContract.EXTRA_EVENT_BEGIN_TIME Czas rozpoczęcia zdarzenia w milisekundach od początku epoki.
CalendarContract.EXTRA_EVENT_END_TIME Czas zakończenia zdarzenia w milisekundach od początku epoki.
CalendarContract.EXTRA_EVENT_ALL_DAY Wartość logiczna wskazująca, że zdarzenie jest całodniowe. Wartość może być true lub false.
Events.EVENT_LOCATION Lokalizacja zdarzenia.
Events.DESCRIPTION Opis zdarzenia.
Intent.EXTRA_EMAIL Adresy e-mail osób, które chcesz zaprosić, jako listy rozdzielonej przecinkami.
Events.RRULE Reguła powtarzania zdarzenia.
Events.ACCESS_LEVEL Określa, czy wydarzenie jest prywatne czy publiczne.
Events.AVAILABILITY czy to wydarzenie jest czasem zajętym czy wolnym, który można zaplanować.

W sekcjach poniżej znajdziesz informacje o sposobach korzystania z tych intencji.

Użyj intencji do wstawienia zdarzenia

Użycie okna dialogowego INSERT pozwala aplikacji przekazać zadanie wstawiania wydarzenia do Kalendarza. W takim przypadku aplikacja nie musi nawet mieć uprawnień WRITE_CALENDARpliku manifestu.

Gdy użytkownicy uruchamiają aplikację, która korzysta z tego podejścia, aplikacja przekierowuje ich do Kalendarza, aby mogli dokończyć dodawanie wydarzenia. Intencje INSERT używają dodatkowych pól, aby wstępnie wypełnić formularz szczegółami wydarzenia w Kalendarzu. Użytkownicy mogą anulować wydarzenie, edytować formularz w razie potrzeby lub zapisać wydarzenie w swoich kalendarzach.

Oto fragment kodu, który planuje zdarzenie na 19 stycznia 2012 r., które będzie trwać od 7:30 do 8:30. Pamiętaj o tych informacjach dotyczących tego fragmentu kodu:

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

Edytowanie zdarzenia za pomocą intencji

Wydarzenie możesz zaktualizować bezpośrednio zgodnie z opisem w artykule Aktualizowanie wydarzeń. Użycie intencji EDIT umożliwia jednak aplikację, która nie ma uprawnień do przekazywania edycji wydarzeń do aplikacji Kalendarz. Gdy użytkownicy skończą edytowanie wydarzenia w Kalendarzu, wrócą do pierwotnej aplikacji.

Oto przykład intencji, która ustawia nowy tytuł dla określonego wydarzenia i pozwala użytkownikom edytować to wydarzenie w Kalendarzu.

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

Korzystanie z intencji do wyświetlania danych z kalendarza

Dostawca kalendarza oferuje 2 sposoby użycia intencji VIEW:

  • Aby otworzyć Kalendarz na daną datę.
  • Wyświetlanie wydarzenia.

Oto przykład, jak otworzyć Kalendarz na konkretny dzień:

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

Oto przykład, jak otworzyć zdarzenie do wyświetlenia:

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

Adaptery synchronizacji

Występują tylko niewielkie różnice w sposobie dostępu do dostawcy Kalendarza przez aplikację i adapter synchronizacji:

  • Adapter synchronizacji musi wskazywać, że jest adapterem synchronizacji, przez ustawienie wartości CALLER_IS_SYNCADAPTER na true.
  • W identyfikatorze URI adapter synchronizacji musi podać parametry zapytania ACCOUNT_NAMEACCOUNT_TYPE.
  • Adapter synchronizacji ma dostęp do zapisu w większej liczbie kolumn niż aplikacja lub widżet. Aplikacja może na przykład zmodyfikować tylko kilka właściwości kalendarza, takich jak nazwa, wyświetlana nazwa, ustawienie widoczności i synchronizacja. Natomiast adapter synchronizacji może mieć dostęp nie tylko do tych kolumn, ale także do wielu innych, takich jak kolor kalendarza, strefa czasowa, poziom dostępu, lokalizacja itp. Jednak adapter synchronizacji jest ograniczony do ACCOUNT_NAME i ACCOUNT_TYPE.

Oto pomocnicza metoda, która zwraca identyfikator URI do użycia z adapterem synchronizacji:

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