Omówienie dostawcy kalendarza

Dostawca kalendarza to repozytorium wydarzeń w kalendarzu użytkownika. Interfejs Calendar Provider API umożliwia wykonywanie zapytań, wstawianie, aktualizowanie i usuwanie działań dotyczących kalendarzy, wydarzeń, uczestników, przypomnień itp.

Interfejs Calendar Provider API może być używany przez aplikacje i adaptery synchronizacji. Zasady różnią się w zależności od typu programu, który przeprowadza połączenia. W tym dokumencie skupiono się głównie na używaniu interfejsu Calendar Provider API jako aplikacji. Informacje o różnicach między adapterami synchronizacji znajdziesz w artykule Adaptery do synchronizacji.

Normalnie, aby odczytywać lub zapisywać dane kalendarza, plik manifestu aplikacji musi zawierać odpowiednie uprawnienia opisane w sekcji Uprawnienia użytkowników. Aby ułatwić wykonywanie typowych operacji, dostawca Kalendarza udostępnia zestaw intencji zgodnie z opisem w sekcji intencje kalendarza. Te intencje kierują użytkowników do aplikacji Kalendarz, gdzie mogą wstawiać, wyświetlać i edytować wydarzenia. Użytkownik wchodzi w interakcję z aplikacją Kalendarz, a następnie wraca do pierwotnej aplikacji. Dzięki temu aplikacja nie musi prosić o uprawnienia ani udostępniać interfejsu użytkownika do wyświetlania czy tworzenia zdarzeń.

Podstawy

Dostawcy treści przechowują dane i udostępniają je aplikacjom. Dostawcy treści oferowani przez platformę Androida (w tym dostawca kalendarza) zazwyczaj udostępniają dane w postaci zbioru tabel na podstawie modelu relacyjnej bazy danych, w którym każdy wiersz jest rekordem, a każda kolumna zawiera dane określonego typu i znaczenia. Za pomocą interfejsu Calendar Provider API aplikacje i adaptery synchronizacji mogą uzyskać uprawnienia do odczytu i zapisu w tabelach bazy danych, w których znajdują się dane kalendarza użytkownika.

Każdy dostawca treści udostępnia publiczny identyfikator URI (zapakowany jako obiekt Uri), który jednoznacznie identyfikuje jego zbiór danych. Dostawca treści, który kontroluje wiele zbiorów danych (wielu tabel), udostępnia oddzielny identyfikator URI dla każdego z nich. Wszystkie identyfikatory URI dostawców zaczynają się od ciągu „content://”. Dzięki temu dane będą kontrolowane przez dostawcę treści. Dostawca kalendarza określa stałe dla identyfikatorów URI dla każdej z jej klas (tabel). Te identyfikatory URI mają format <class>.CONTENT_URI. Przykład: Events.CONTENT_URI.

Rysunek 1 przedstawia model danych dostawcy kalendarza w formie graficznej. Widoczne są w nim 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, a różne kalendarze mogą być powiązane z różnymi typami kont (Kalendarza Google, Exchange itd.).

CalendarContract określa model danych informacji związanych z kalendarzem i wydarzeniami. Te dane są przechowywane w szeregu tabel wymienionych poniżej.

Tabela (klasa) Opis

CalendarContract.Calendars

Zawiera ona informacje specyficzne dla kalendarza. Każdy wiersz w tej tabeli zawiera szczegóły pojedynczego kalendarza, np. nazwę, kolor, informacje o synchronizacji itp.
CalendarContract.Events Zawiera ona informacje o zdarzeniach. Każdy wiersz w tej tabeli zawiera informacje o pojedynczym wydarzeniu, np. tytuł wydarzenia, lokalizacja, godzina rozpoczęcia, godzina zakończenia itd. Wydarzenie może wystąpić jednorazowo lub wielokrotnie. Uczestnicy, przypomnienia i dodatkowe właściwości są przechowywane w osobnych tabelach. Każdy z nich ma atrybut EVENT_ID, który odwołuje się do elementu _ID w tabeli Zdarzenia.
CalendarContract.Instances Ta tabela zawiera godziny rozpoczęcia i zakończenia każdego wystąpienia zdarzenia. Każdy wiersz w tej tabeli odpowiada pojedynczemu wystąpieniu zdarzenia. W przypadku zdarzeń jednorazowych następuje mapowanie instancji na zdarzenia w sposób 1:1. W przypadku wydarzeń cyklicznych automatycznie wygenerujemy wiele wierszy, które odpowiadają wielokrotnym wystąpieniu danego wydarzenia.
CalendarContract.Attendees Ta tabela zawiera informacje o uczestnikach (gościu). Każdy wiersz odpowiada jednemu gościowi wydarzenia. Określa on typ gościa i jego odpowiedź na temat uczestniczenia w wydarzeniu.
CalendarContract.Reminders Ta tabela zawiera dane alertów/powiadomień. Każdy wiersz odpowiada jednemu alertowi dotyczącemu zdarzenia. Każde wydarzenie może mieć kilka przypomnień. Maksymalna liczba przypomnień na wydarzenie jest określana w polu MAX_REMINDERS i ustawiana przez adapter synchronizacji, do którego należy dany kalendarz. Przypomnienia są ustawiane na minuty przed wydarzeniem i mają metodę, która określa, w jaki sposób użytkownik będzie powiadamiany.

Interfejs API dostawcy kalendarza został zaprojektowany z myślą o elastyczności i zaawansowanych możliwościach. Jednocześnie ważne jest, aby użytkownicy byli zadowoleni z korzystania z usługi i chronili integralność kalendarza oraz jego danych. Dlatego podczas korzystania z interfejsu API warto pamiętać o kilku kwestiach:

  • Wstawianie, aktualizowanie i wyświetlanie wydarzeń w kalendarzu. Aby bezpośrednio wstawiać, modyfikować i odczytywać wydarzenia z dostawcy kalendarza, musisz mieć odpowiednie uprawnienia. Jeśli jednak nie tworzysz pełnej aplikacji kalendarza ani adaptera synchronizacji, żądanie tych uprawnień nie jest konieczne. Zamiast tego możesz użyć intencji obsługiwanych przez aplikację Kalendarz na Androidzie do przekazywania operacji odczytu i zapisu w tej aplikacji. Gdy używasz intencji, Twoja aplikacja wysyła użytkowników do aplikacji Kalendarz, aby wykonać żądaną operację we wstępnie wypełnionym formularzu. Gdy to zrobią, zostaną przywrócone do aplikacji. Aplikacja zaprojektowana do wykonywania typowych operacji w Kalendarzu zapewnia użytkownikom spójny i niezawodny interfejs. To zalecane podejście. Więcej informacji znajdziesz w artykule o intencjach Kalendarza.
  • Synchronizuj 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 występują kolumny, które są zarezerwowane dla adapterów synchronizacji. Dostawca ani aplikacje nie powinny ich modyfikować. W rzeczywistości nie są one widoczne bez dostępu jako adapter synchronizacji. Więcej informacji o adapterach synchronizacji znajdziesz w artykule Adaptery synchronizacji.

Uprawnienia użytkownika

Aby umożliwić odczytywanie danych kalendarza, aplikacja musi umieścić w swoim pliku manifestu uprawnienie READ_CALENDAR. Musi zawierać uprawnienie WRITE_CALENDAR umożliwiające usuwanie, wstawianie lub aktualizowanie 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. Poniższe kolumny kalendarzy umożliwiają zapis przez aplikację i adapter 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 jest widoczna dla użytkownika.
VISIBLE Wartość logiczna wskazująca, czy kalendarz został wybrany do wyświetlenia. Wartość 0 oznacza, że wydarzenia powiązane z tym kalendarzem nie będą wyświetlane. Wartość 1 oznacza, że powinny być wyświetlane wydarzenia powiązane z tym kalendarzem. Ta wartość wpływa na generowanie wierszy w tabeli CalendarContract.Instances.
SYNC_EVENTS Wartość logiczna wskazująca, czy kalendarz powinien być synchronizowany i czy jego zdarzenia mają być przechowywane na urządzeniu. Wartość 0 oznacza, że kalendarz nie synchronizuje się z tym kalendarzem ani nie są tam przechowywane jego wydarzenia. Wartość 1 oznacza synchronizację wydarzeń w tym kalendarzu i zapisywanie wydarzeń na urządzeniu.

Podaj typ konta dla wszystkich operacji

Jeśli wysyłasz zapytanie na Calendars.ACCOUNT_NAME, musisz też uwzględnić w zaznaczeniu parametr Calendars.ACCOUNT_TYPE. Dzieje się tak, ponieważ dane konto jest uważane za niepowtarzalne tylko wtedy, gdy jego właściwości ACCOUNT_NAME i ACCOUNT_TYPE są prawidłowe. ACCOUNT_TYPE to ciąg znaków odpowiadający mechanizmowi uwierzytelniania konta użytemu podczas rejestracji konta w: AccountManager. Istnieje też specjalny rodzaj konta o nazwie ACCOUNT_TYPE_LOCAL w przypadku kalendarzy niepowiązanych z kontem urządzenia. ACCOUNT_TYPE_LOCAL konta nie są synchronizowane.

Tworzenie zapytania do kalendarza

Oto przykład, który pokazuje, jak uzyskać kalendarze należące do określonego użytkownika. Dla uproszczenia w tym przykładzie operacja zapytania jest pokazywana w wątku interfejsu („wątek główny”). W praktyce należy to zrobić w wątku asynchronicznym, a nie w wątku głównym. Więcej o tym dowiesz się w sekcji Ładujące. Jeśli chcesz nie tylko odczytywać dane, ale też je modyfikować, zapoznaj się z sekcją 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 utworzysz zapytanie. Ten wybór określa kryteria zapytania. W tym przykładzie zapytanie szuka kalendarzy z adresami ACCOUNT_NAME „hera@example.com”, ACCOUNT_TYPE „com.example” i OWNER_ACCOUNT„hera@example.com”. Jeśli chcesz zobaczyć wszystkie kalendarze przeglądane przez użytkownika, a nie tylko te, które należą do niego, pomiń OWNER_ACCOUNT. Zapytanie zwraca obiekt Cursor, którego możesz użyć do przeszukiwania zbioru wyników zwróconego przez zapytanie do bazy danych. Więcej o używaniu zapytań u dostawców treści dowiesz się z artykułu 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 będzie można poruszać się po zbiorze wyników za pomocą kursora. Do zwracania wartości dla poszczególnych pól wykorzystuje stałe ustawione 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ć jego _ID jako dołączany identyfikator do identyfikatora URI (withAppendedId()) lub jako pierwszy element wyboru. Powinien zaczynać się od "_id=?", a pierwszy selectionArg powinien być wartością _ID kalendarza. Możesz też wprowadzić zmiany, kodując identyfikator w identyfikatorze URI. W tym przykładzie zmienia się wyświetlana nazwa kalendarza za pomocą metody 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

Do zarządzania kalendarzami służy przede wszystkim adapter synchronizacji, więc nowe kalendarze należy wstawiać 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ć kalendarz lokalny, może to zrobić przez wstawienie kalendarza jako adapter synchronizacji przy użyciu polecenia ACCOUNT_TYPE o wartości ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCAL to specjalny typ konta do obsługi kalendarzy, które nie są powiązane z kontem urządzenia. Kalendarze tego typu nie są synchronizowane z serwerem. Więcej informacji o adapterach synchronizacji znajdziesz w artykule Adaptery synchronizacji.

Tabela zdarzeń

Tabela CalendarContract.Events zawiera szczegółowe informacje o poszczególnych zdarzeniach. Aby dodawać, aktualizować i usuwać zdarzenia, aplikacja musi zawierać uprawnienie WRITE_CALENDAR w swoim pliku manifestu.

Poniższe kolumny zdarzeń są dostępne do zapisu zarówno przez 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 Nazwa 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 czasu zakończenia wydarzenia.
DURATION Czas trwania zdarzenia w formacie RFC5545. Na przykład wartość "PT1H" oznacza, że wydarzenie powinno trwać godzinę, a wartość "P2W" oznacza czas trwania wynoszący 2 tygodnie.
ALL_DAY Wartość 1 oznacza, że wydarzenie zajmuje cały dzień, zgodnie z definicją w lokalnej strefie czasowej. Wartość 0 oznacza, że jest to zwykłe wydarzenie, które może się rozpocząć i zakończyć w dowolnym momencie w ciągu dnia.
RRULE Reguła powtarzania formatu zdarzenia. Przykład: "FREQ=WEEKLY;COUNT=10;WKST=SU". Więcej przykładów znajdziesz tutaj.
RDATE Daty powtarzania zdarzenia. Typu RDATE zwykle używa się w połączeniu z atrybutem RRULE, by zdefiniować zbiorczy zestaw powtarzających się wystąpień. Więcej informacji na ten temat znajdziesz w specyfikacji RFC5545.
AVAILABILITY Czy to wydarzenie liczy się jako czas zajętości lub wolny, który można zaplanować.
GUESTS_CAN_MODIFY Określa, czy goście mogą modyfikować wydarzenie.
GUESTS_CAN_INVITE_OTHERS Określa, 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 zgodnie z opisem w sekcji Używanie intencji w celu wstawienia zdarzenia. Jeśli jednak chcesz, możesz wstawić zdarzenia bezpośrednio. Z tej sekcji dowiesz się, jak to zrobić.

Oto reguły wstawiania nowego zdarzenia:

Oto przykład wstawiania zdarzenia. Dla uproszczenia wykonywana jest to w wątku UI. W praktyce wstawienia i aktualizacje powinny być wykonywane w wątku asynchronicznym, aby przenieść działanie do wątku w tle. 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: zobacz, jak w tym przykładzie jest przechwytywany identyfikator zdarzenia po jego utworzeniu. To najprostszy sposób na uzyskanie identyfikatora wydarzenia. Identyfikator wydarzenia jest często potrzebny do wykonywania innych operacji w kalendarzu, na przykład do dodawania uczestników lub przypomnień do wydarzeń.

Aktualizacje

Jeśli aplikacja chce zezwolić użytkownikowi na edytowanie zdarzenia, zalecamy użycie intencji EDIT zgodnie z opisem w sekcji Używanie intencji edytowania zdarzenia. Jeśli jednak chcesz, możesz edytować zdarzenia bezpośrednio. Aby zaktualizować zdarzenie, możesz podać jego _ID jako dołączany identyfikator do identyfikatora URI (withAppendedId()) lub jako pierwszy element wyboru. Wybór powinien zaczynać się od "_id=?", a pierwszy element selectionArg powinien być wartością _ID zdarzenia. Możesz też wprowadzić zmiany, używając zaznaczenia bez identyfikatora. Oto przykład aktualizacji zdarzenia. Zmienia tytuł wydarzenia w ten sposób: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

Możesz usunąć zdarzenie, korzystając z jego _ID jako dołączonego identyfikatora w identyfikatorze URI lub korzystając z wyboru standardowego. Jeśli użyjesz dołączonego identyfikatora, nie możesz też dokonać wyboru. Istnieją dwie wersje usuwania: jako aplikacja i jako adapter synchronizacji. Usunięcie aplikacji ustawia w kolumnie deleted 1. Ta flaga informuje adapter synchronizacji, że wiersz został usunięty i że usunięcie powinno zostać rozpowszechnione na serwerze. Usunięcie adaptera synchronizacji usuwa zdarzenie z bazy danych wraz ze wszystkimi powiązanymi danymi. Oto przykład aplikacji usuwającej zdarzenie za pomocą _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 pojedynczemu uczestnikowi lub gościowi wydarzenia. Wywołanie query() zwraca listę uczestników wydarzenia z podaną wartością EVENT_ID. Ta wartość EVENT_ID musi być zgodna z wartością _ID konkretnego zdarzenia.

W tabeli poniżej znajdziesz pola, które można zapisać. Gdy wstawiasz nowego uczestnika, musisz uwzględnić ich wszystkich oprócz ATTENDEE_NAME.

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

Relacja uczestnika do wydarzenia. Jedna z tych możliwości:

ATTENDEE_TYPE

Typ uczestnika. Jedna z tych możliwości:

ATTENDEE_STATUS

Stan obecności uczestnika. Jedna z tych możliwości:

Dodaj uczestników

Oto przykład dodawania jednego uczestnika do wydarzenia. Pamiętaj, że właściwość EVENT_ID jest wymagana:

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 1 przypomnieniu o wydarzeniu. Wywołanie query() zwraca listę przypomnień o wydarzeniu z podaną wartością EVENT_ID.

W tabeli poniżej znajdziesz pola przypomnień, które można zapisać. Wszystkie muszą być uwzględnione przy wstawianiu nowego przypomnienia. Pamiętaj, że adaptery synchronizacji określają typy przypomnień obsługiwanych w tabeli CalendarContract.Calendars. Aby dowiedzieć się więcej, wejdź na ALLOWED_REMINDERS.

Stała Opis
EVENT_ID Identyfikator zdarzenia.
MINUTES Minuty przed wydarzeniem, na które powinno zostać uruchomione przypomnienie.
METHOD

Metoda alarmu ustawiona na serwerze. Jedna z tych możliwości:

Dodawaj przypomnienia

Ten przykład pozwala dodać przypomnienie do wydarzenia. Przypomnienie uruchamia się 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 pojedynczemu wystąpieniu zdarzenia. W tabeli instancji nie ma możliwości zapisu i umożliwia ona jedynie wysyłanie zapytań o wystąpienia zdarzeń.

W tabeli poniżej znajdziesz niektóre pola, o które możesz wykonywać zapytania dotyczące 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 czasu UTC.
END Czas zakończenia instancji instancji w milisekundach w czasie UTC.
END_DAY Juliański dzień zakończenia instancji względem strefy czasowej Kalendarza.
END_MINUTE Minuta końcowa instancji mierzona od północy w strefie czasowej Kalendarza.
EVENT_ID Wartość _ID zdarzenia dla tej instancji.
START_DAY Juliański dzień rozpoczęcia instancji określony według strefy czasowej Kalendarza.
START_MINUTE Minuta rozpoczęcia instancji mierzona od północy zgodnie ze strefą czasową kalendarza.

Utwórz zapytanie do tabeli instancji

Aby przesłać zapytanie do tabeli Instancje, musisz w identyfikatorze URI określić zakres czasu dla zapytania. W tym przykładzie CalendarContract.Instances uzyskuje dostęp do pola TITLE przez wdrożenie interfejsu CalendarContract.EventsColumns. Inaczej mówiąc, funkcja TITLE jest zwracana w widoku bazy danych, a nie przez wysłanie zapytania 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 kalendarza

Twoja aplikacja nie potrzebuje uprawnień do odczytu i zapisu danych w kalendarzu. Zamiast tego może on wykorzystać intencje obsługiwane przez aplikację Kalendarz na Androidzie do przekazywania operacji odczytu i zapisu w tej aplikacji. W tabeli poniżej znajdziesz intencje obsługiwane przez dostawcę kalendarza:

Działanie URI Opis Dodatkowe treści

VIEW

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

Możesz też odwołać się do identyfikatora URI za pomocą właściwości CalendarContract.CONTENT_URI. Przykład użycia tej intencji znajdziesz w sekcji Używanie intencji do wyświetlania danych kalendarza.
Otwórz kalendarz na godzinę określoną przez <ms_since_epoch>. Brak.

VIEW

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

Możesz też odwołać się do identyfikatora URI za pomocą właściwości Events.CONTENT_URI. Przykład użycia tej intencji znajdziesz w sekcji Używanie intencji do wyświetlania danych kalendarza.
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ą właściwości Events.CONTENT_URI. Przykład użycia tej intencji znajdziesz w sekcji Używanie intencji do edytowania zdarzenia.
Edytuj zdarzenie określone przez <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

Możesz też odwołać się do identyfikatora URI za pomocą właściwości Events.CONTENT_URI. Przykład użycia tej intencji znajdziesz w sekcji Używanie intencji do wstawienia zdarzenia.
Utwórz wydarzenie. Wszystkie dodatki wymienione w tabeli poniżej.

Poniższa tabela przedstawia dodatki intencji obsługiwane przez dostawcę kalendarza:

Dodatkowa intencja Opis
Events.TITLE Nazwa zdarzenia.
CalendarContract.EXTRA_EVENT_BEGIN_TIME Godzina 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 wydarzenie trwa cały dzień. Wartością może być true lub false.
Events.EVENT_LOCATION Lokalizacja wydarzenia.
Events.DESCRIPTION Opis zdarzenia.
Intent.EXTRA_EMAIL Adresy e-mail osób, które mają zostać zaproszone, podane na liście rozdzielonej przecinkami.
Events.RRULE Reguła powtarzania zdarzenia.
Events.ACCESS_LEVEL Określa, czy wydarzenie jest prywatne czy publiczne.
Events.AVAILABILITY Czy to wydarzenie liczy się jako czas zajętości lub jest wolny, który można zaplanować.

W sekcjach poniżej opisano, jak korzystać z tych intencji.

Wstawianie zdarzenia za pomocą intencji

Dzięki intencji INSERT aplikacja może przekazywać zadanie wstawiania wydarzeń do kalendarza. Przy tym zastosowaniu aplikacja nie musi nawet mieć uprawnienia WRITE_CALENDAR w pliku manifestu.

Gdy użytkownicy uruchamiają aplikację korzystającą z tego podejścia, wysyła ona ich do Kalendarza, aby dokończyć dodawanie wydarzenia. Intencja INSERT używa dodatkowych pól do wstępnego wypełnienia formularza szczegółami wydarzenia w Kalendarzu. Użytkownicy mogą anulować wydarzenie, edytować formularz lub zapisać wydarzenie w swoich kalendarzach.

Oto fragment kodu, który pozwala zaplanować wydarzenie na 19 stycznia 2012 r. od 7:30 do 8:30. Zwróć uwagę na te kwestie:

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

Zdarzenie możesz zaktualizować bezpośrednio w sposób opisany w sekcji Aktualizowanie zdarzeń. Jednak użycie intencji EDIT umożliwia aplikacji, która nie ma uprawnień do przekazywania edycji wydarzeń do aplikacji Kalendarz. Gdy użytkownicy zakończą edytowanie wydarzenia w Kalendarzu, zostaną przeniesieni do oryginalnej aplikacji.

Oto przykład intencji, która ustawia nowy tytuł 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);

Używaj intencji związanych z wyświetlaniem danych kalendarza

Dostawca kalendarza udostępnia 2 różne sposoby korzystania z intencji VIEW:

  • Aby otworzyć Kalendarz z konkretną datą.
  • Aby wyświetlić wydarzenie:

Oto przykład, który pokazuje, jak otworzyć Kalendarz z konkretną datą:

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, który pokazuje, jak otworzyć wydarzenie do wyświetlania:

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 drobne różnice w dostępie aplikacji i adaptera synchronizacji do dostawcy kalendarza:

  • Adapter synchronizacji musi określić, że jest to adapter synchronizacji, ustawiając CALLER_IS_SYNCADAPTER na true.
  • Adapter synchronizacji musi udostępnić parametry ACCOUNT_NAME i ACCOUNT_TYPE jako parametry zapytania w identyfikatorze URI.
  • Adapter synchronizacji ma uprawnienia do zapisu w większej liczbie kolumn niż aplikacja lub widżet. Na przykład aplikacja może modyfikować tylko kilka elementów kalendarza, takich jak nazwa, wyświetlana nazwa, ustawienie widoczności i to, czy kalendarz jest synchronizowany. Dla porównania adapter synchronizacji ma dostęp nie tylko do tych kolumn, ale też do wielu innych, takich jak kolor kalendarza, strefa czasowa, poziom dostępu, lokalizacja itd. Adapter synchronizacji jest jednak ograniczony do określonych przez niego ACCOUNT_NAME i ACCOUNT_TYPE.

Oto metoda pomocnicza, która pozwala zwrócić identyfikator URI na potrzeby adaptera 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();
 }