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ą.
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 |
---|---|
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
iCalendarContract.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_LOCAL
to 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:
- Musisz uwzględnić
CALENDAR_ID
iDTSTART
. - Musisz podać
EVENT_TIMEZONE
. Aby uzyskać listę zainstalowanych identyfikatorów stref czasowych systemu, użyjgetAvailableIDs()
. Pamiętaj, że ta reguła nie ma zastosowania, jeśli wstawiasz zdarzenie za pomocą intencjiINSERT
, jak opisano w sekcji Wstawianie zdarzenia za pomocą intencji. W takim przypadku podawana jest domyślna strefa czasowa. - W przypadku wydarzeń niecyklicznych musisz dodać
DTEND
. - W przypadku wydarzeń cyklicznych musisz uwzględnić
DURATION
opróczRRULE
lubRDATE
. Pamiętaj, że ta reguła nie ma zastosowania, jeśli wstawiasz zdarzenie za pomocą intencjiINSERT
, opisanej w artykule Wstawianie zdarzenia za pomocą intencji. W takim przypadku możesz użyć wartościRRULE
w połączeniu z wartościamiDTSTART
iDTEND
, a aplikacja Kalendarz automatycznie przekształci ją w czas trwania.
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 EDIT
intencji, 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_ID
musi 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 |
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. |
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 |
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 |
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_CALENDAR
w pliku 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:
- Jako adres Uri podaj
Events.CONTENT_URI
. - Używa on dodatkowych pól
CalendarContract.EXTRA_EVENT_BEGIN_TIME
iCalendarContract.EXTRA_EVENT_END_TIME
, aby wstępnie wypełnić formularz godziną zdarzenia. Wartości te muszą być podane w milisekundach UTC od epoki. - Wykorzystuje ono dodatkowe pole
Intent.EXTRA_EMAIL
do utworzenia oddzielonej przecinkami listy zaproszonych osób, które zostały określone za pomocą adresu e-mail.
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
natrue
. - W identyfikatorze URI adapter synchronizacji musi podać parametry zapytania
ACCOUNT_NAME
iACCOUNT_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
iACCOUNT_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(); }