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ą.
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 |
---|---|
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
iCalendarContract.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:
- Musisz uwzględnić
CALENDAR_ID
iDTSTART
. - Musisz uwzględnić pole
EVENT_TIMEZONE
. Aby uzyskać listę identyfikatorów strefy czasowej zainstalowanych w systemie, użyj poleceniagetAvailableIDs()
. Ta reguła nie ma zastosowania, jeśli zdarzenie wstawiasz przez intencjęINSERT
zgodnie z opisem w sekcji Używanie intencji w celu wstawienia zdarzenia – w tym scenariuszu jest podana domyślna strefa czasowa. - W przypadku wydarzeń niecyklicznych musisz uwzględnić wartość
DTEND
. - W przypadku wydarzeń cyklicznych oprócz
RRULE
lubRDATE
musisz podać teżDURATION
. Ta reguła nie ma zastosowania, jeśli wstawiasz zdarzenie przez intencjęINSERT
zgodnie z opisem w sekcji Używanie intencji do wstawiania zdarzenia. W tym scenariuszu możesz użyć właściwościRRULE
w połączeniu z elementamiDTSTART
iDTEND
, a aplikacja Kalendarz automatycznie zmieni czas trwania na czas trwania.
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 |
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. |
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 |
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 |
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:
- Jako identyfikator URI wskazuje
Events.CONTENT_URI
. - Wykorzystuje dodatkowe pola
CalendarContract.EXTRA_EVENT_BEGIN_TIME
iCalendarContract.EXTRA_EVENT_END_TIME
, aby wstępnie wypełnić formularz godziną wydarzenia. Wartości czasu muszą być podane w milisekundach w czasie UTC od początku epoki. - W dodatkowym polu
Intent.EXTRA_EMAIL
znajduje się lista zaproszonych osób (lista adresów e-mail oddzielonych przecinkami).
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
natrue
. - Adapter synchronizacji musi udostępnić parametry
ACCOUNT_NAME
iACCOUNT_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
iACCOUNT_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(); }