Oficjalne API do kalendarza to była kolejna istotna nowość Android 4.0. Windows Phone 7.5 miał zdaje się po raz pierwszy obsługę takich rzeczy, też za pomocą zapytań, aczkolwiek jakoś bardziej wygodnych. Windows i Windows Phone 8.1 funcjonalności odnośnie kalendarza mają podobne, czyli dodawanie, edycja, usuwanie, listowanie itp. Też możemy wykonywać operacje za pomocą systemowego UI. Możliwość definiowania własnych kalendarzy również została wprowadzona. Wracając do Androida to jego intencje dla kalendarza to jakby odpowiednik systemowych tasków. Content provider z operacjami na tabelkach to znacznie bardziej złożone rozwiązanie, ale ujdzie. Nie rozumiem tylko po co obnażanie wnętrzności i danie możliwości znalezienia eventów, które ktoś usunął, ale nie zostały jeszcze fizycznie wyczyszczone.
Przed Android 4.0 nie było oficjalnego API - przez standardowy content provider trzeba było używać niskopoziomowych danych
Android 4.0
- Klasa CalendarContract dla ułatwienia dostępu
- Wiele często używanych operacji za pomocą intencji
- Bezpośrednie programowanie danych kalendarza nadal wymaga content providera (CalendarContract upraszcza korzystanie z niego)
CalendarContract
centralny punkt dla wszystkich stałych kalendarza
- definiuje uri identyfikujące dane powiązane z kalendarzem
- używane przez klucz-wartość intencji
- nazwy tabel dla content providera
- definiuje nazwy dla elementów powiązanych z kalendarzem
- identyfikuje extras intencji
- identyfikuje kolumny tabel
Intencje kalendarza
- zapewniają najprostszy dostęp do kalendarza
- zalety w stosunku do korzystania z content providera kalendarza
- prezentują użytkownikowi standardowy interfejs kalendarza (startActivity z odpowiednim intent)
- nie wymagają specjalnych pozwoleń
- nie trzeba znać szczegółów storage
- kilka ograniczeń
- wymagają interakcji użytkownika do zakończenia akcji
- nie dostarczają informacji z powrotem do naszej aplikacji
Otwieranie kalendarza z podanym czasem i datą
- akcja: Intent.ACTION_VIEW
- dane: CalendarContract.CONTENT_URI (dodajemy time w ms od północy 1 stycznia 1970)
- content://com.android.calendar/time/1334168996092
Calendar dateToShow = Calendar.getInstance();
dateToShow.set(2014, Calendar.SEPTEMBER, 17, 8, 00);
long ms = dateToShow.getTimeInMillis();
Uri.Builder uriBuilder = CalendarContract.CONTENT_URI.buildUpon();
uriBuilder.appendPath(“time”);
ContentUris.appendId(uriBuilder, ms);
Uri uri = uriBuilder.build();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
Przeglądanie i modyfikacja eventów z intencjami
- dane: CalendarContract.Events.CONTENT_URI (dołączamy event id)
- content://com.android.calendar/events/125
- oglądanie istniejącego eventu: akcja = Intent.ACTION_VIEW
- edycja istniejącego eventu: akcja = Intent.ACTION_EDIT
Uri uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT).setData(uri);
startActivity(intent);
Tworzenie nowych eventów
- akcja: Intent.ACTION_INSERT
- dane: CalendarContract.Events.CONTENT_URI
- uzupełnienie kilku pól (dodanie extras w intent)
- Events.TITLE
- Events.DESCRIPTION
- Events.EVENT_LOCATION
- Intent.EXTRA_EMAIL
- CalendarContract.EXTRA_EVENT_BEGIN_TIME
- CalendarContract.EXTRA_EVENT_END_TIME
- http://developer.android.com/guide/topics/providers/calendar-provider.html#intents
Calendar begin = Calendar.getInstance();
begin.set(2014, Calendar.SEPTEMBER, 17, 8, 0);
Calendar end = Calendar.getInstance();
end.set(2014, Calendar.NOVEMBER, 20, 20, 0);
Intent intent = new Intent(Intent.ACTION_INSERT).setData(CalendarContract.Events.CONTENT_URI);
intent.putExtra(CalendarContract.Events.TITLE, title);
intent.putExtra(CalendarContract.Events.EVENT_LOCATION, location);
intent.putExtra(CalendarContract.Events.DESCRIPTION, description);
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin.getTimeInMillis());
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end.getTimeInMillis());
startActivity(intent);
Calendar content provider
Najbardziej bezpośrednia kontrola nad danymi kalendarza
- zalety w stosunku do intencji
- dostęp do wszystkich danych kalendarza
- dostęp do programów i modyfikacja danych bez interakcji z użytkownikiem
- wyzwania
- deklaracja przez naszą aplikację specyficznych dla kalendarza uprawnień
Uprawnienia content providera kalendarza
- odczytywanie zawartości: “android.permission.READ_CALENDAR”
- zapisywanie zawartości: “android.permission.WRITE_CALENDAR” (prawo do zapisu nie oznacza prawa do odczytu)
Główne klasy CalendarContract
- CalendarContract.Calendars - wszystkie kalendarze
- CalendarContract.Events - wszystkie eventy
- CalendarContract.Instances - czas rozpoczęcia i zakończenia każdego wydarzenia
- CalendarContract.Attendees - osoby uczestniczące w każdym wydarzeniu
- CalendarContract.Reminders - przypominanie o eventach
Calendars –> Events –> Instances, Attendees, Reminders
- Calendars: CalendarContract.Calendars.CONTENT_URI
- Events: CalendarContract.Events.CONTENT_URI, CalendarContract.Events.CALENDAR_ID
- Instances: CalendarContract.Instances.CONTENT_URI, CalendarContract.Instances.EVENT_ID
- Attendees: CalendarContract.Attendees.CONTENT_URI, CalendarContract.Attendees.EVENT_ID
- Reminders: CalendarContract.Reminders.CONTENT_URI, CalendarContract.Reminders.EVENT_ID
Tabela kalendarzy
Lista wszystkich kalendarzy na urządzeniu
- unikalne ID dla każdego kalendarza: CONTENT_URI & _ID
- możliwość wyszukiwania za pomocą ContentResolver.query
- wyszukiwanie konkretnego kalendarza wymaga kombinacji trzech pól
- CalendarContract.Calendars.ACCOUNT_NAME xxx@gmail.com
- CalendarContract.Calendars.ACCOUNT_TYPE
- “com.google” - konta synchronizowane przez Google Calendar
- CalendarContract.Calendars.ACCOUNT_TYPE_LOCAL - dla kalendarzy niesynchronizowanych
- CalendarContract.Calendars.NAME - nazwa kalendarza, kiedy został utworzony
long calendarID = 1;
Uri uri = ContentUris.withAppendedId(CalendarContract.CONTENT_URI, calendarID);
Listowanie kalendarzy
String[] returnColumns = new String[] {
CalendarContract.Calendars._ID,
CalendarContract.Calendars.ACCOUNT_NAME,
CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,
CalendarContract.Calendars.ACCOUNT_TYPE
};
Cursor cursor = null;
ContentResolver cr = getContentResolver();
cursor = cr.query(CalendarContract.Calendars.CONTENT_URI, returnColumns, null, null, null);
while (cursor.moveToNext()) {
long calID = cursor.getLong(0);
String displayName = cursor.getString(1);
String accountName = cursor.getString(2);
String accountType = cursor.getString(3);
…
}
cursor.close();
Znalezienie kalendarza
String[] returnColumns = new String[] {
CalendarContract.Calendars._ID
};
String selection = “((“ + CalendarContract.Calendars.ACCOUNT_NAME + “ = ?) AND ” +
“(“ + CalendarContract.Calendars.ACCOUNT_TYPE + “ = ?) AND ” +
“(“ + CalendarContract.Calendars.CALENDAR_DISPLAY_NAME + “ = ?))”;
String[] selectionArgs = {accountName, accountType, displayName};
Cursor cursor = null;
ContentResolver cr = getContentResolver();
cursor = cr.query(CalendarContract.Calendars.CONTENT_URI, returnColumns, selection, selectionArgs, null);
long calendarID = –1;
if (cursor.moveToNext())
calendarID = cursor.getLong(0);
return calendarID;
Tabela zdarzeń
Lista wszystkich zdarzeń dla wszystkich kalendarzy
- Identyfikacja zdarzeń
- bezpośredni dostęp przez unikalne _ID
- powiązanie z kalendarzem za pomocą kolumny CALENDAR_ID
- Wyszukiwanie przez ContentResolver.query
- Ważne kolumny
- TITLE
- EVENT_LOCATION
- DESCRIPTION
- DTSTART: czas rozpoczęcia (ms od północy 1 stycznia 1970)
- DTEND: czas zakończenia (-//-)
- EVENT_TIMEZONE: nazwa strefy czasowej miejsca, gdzie odbywa się zdarzenie np. US/Eastern (lista wszystkich dostępnych – TimeZone.getAvailableIDs())
Dodawanie nowych zdarzeń
Dodawanie za pomocą ContentResolver.insert
- Wartości dostarczone w tablicy ContentValues
- Wymagane kolumny
- CALENDER_ID
- DTSTART
- DTEND (dla niecyklicznych)
- EVENT_TIMEZONE
- zwracane jest URI nowego zdarzenia (można wyciągnąć id zdarzenia przy pomocy ContentUris.parseId)
Modyfikacja i usuwanie zdarzeń
- Modyfikacja za pomocą ContentResolver.update
- idetyfikacja zdarzeń do update’u przy użyciu event URI lub query
- specyfikacja kolumn do zmiany i ich wartości przy użyciu klasy ContentValues
- Usuwamy za pomocą ContentResolver.delete
- identyfikacja zdarzeń do zmiany przy użyciu event URI lub query
- skasowane zdarzenia mogą nie być natychmiast usunięte
- zdarzenie nie jest usuwane dopóki urządzenie nie zsynchronizuje się z serwerem
- należy sprawdzić, czy kolumna DELETED ma wartość 0, aby być pewnym że zdarzenie jest nadal poprawne
Aktualizacja zdarzenia
ContentResolver cr = getContentResolver();
Uri uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID);
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.EVENT_LOCATION, location);
values.put(CalendarContract.Events.DESCRIPTION, description);
int rows = 0;
rows = cr.update(uri, values, null, null);
Usuwanie zdarzenia
ContentResolver cr = getContentResolver();
Uri uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID);
int rows = 0;
rows = cr.delete(uri, null, null);
Sprawdzenie czy event został naprawdę usunięty
String[] EVENT_PROJECTION = new String[] {
CalendarContract.Events._ID,
CalendarContract.Events.TITLE,
CalendarContract.Events.DELETED
};
String queryFilter = CalendarContract.Events.DELETED + “ = ?”;
String[] queryFilterValues = {“0”};
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID);
cur = cr.query(uri, EVENT_PROJECTION, queryFilter, queryFilterValues, null);
if (cur.moveToNext()) {
long returnedEventID = cur.getLong(0);
String title = cur.getString(1);
int isDeleted = cur.getInt(2);
…
}
Uczestnicy eventów
Tabela Attendees
- Uczestnika dodajemy do wydarzenia przy użyciu ContentResolver.insert (dla każdego uczestnika musimy podać prawidłowe event id)
- Ważne kolumny
- ATTENDEE_NAME
- ATTENDEE_EMAIL
- ATTENDEE_RELATIONSHIP (relacja do użytkownika, stałe Attendees.RELATIONSHIP_xxx: Attendee, Organizer, Performer, Speaker)
- ATTENDEE_TYPE (stałe Attendees.TYPE_xxx: Optional, Required, Resource, dozwolone typy Calendars.ALLOWED_ATTENDEE_TYPES)
- ATTENDEE_STATUS (stałe Attendees.STATUS_xxx: Accepted, Declined, Invited, Tentative)
Przypominanie o wydarzeniach
- Dodajemy przypomnienie do spotkania używając ContentResolver.insert
- trzeba podać poprawne event id
- limit na kalendarz: Calendars.MAX_REMINDERS
- Ważne kolumny
- METHOD: typ (stałe Reminders.METHOD_xxx: Alert, Default, Email, SMS)
- MINUTES: minuty do rozpoczęcia alarmu
Zdarzenia cykliczne
Informacje o cykliczności w tabeli Instances
- Kolumny dla dat/czasu cyklów eventów
- Zapytania zawsze muszą mieć podany przedział czasu (dołączamy czas początkowy i końcowy w CONTENT_URI)
- Widok na wystąpienia eventu
- Tabela może być tylko odpytywana
- Nie można utworzyć powtórzeń
Uri.Builder uriBuilder = CalendarContract.Instances.CONTENT_URI.buildUpon();
ContentUris.appendedId(uriBuilder, startDate.getTimeInMillis());
ContentUris.appendedId(uriBuilder, endDate.getTimeInMillis());
return uriBuilder.build();
Tworzenie cyklicznych eventów
Cykliczność jest cechą zdarzenia
- Events.DTSTART - start pierwszego wystąpienia
- Events.DURATION - długość każdego wystąpienia w formacie iCalendar (http://tools.ietf.org/html/rfc5545#section-3.3.6), zamiast DTEND
- Events.RRULE - informacja o cykliczności w formacie iCalendar (http://tools.ietf.org/html/rfc5545#section-3.8.5.3)
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.CALENDAR_ID, calendarID);
values.put(CalendarContract.Events.TITLE, title);
values.put(CalendarContract.Events.DESCRIPTION, description);
values.put(CalendarContract.Events.EVENT_LOCATION, location);
values.put(CalendarContract.Events.DTSTART, startDate.getTimeInMillis());
values.put(CalendarContract.Events.EVENT_TIMEZONE, “US/Eastern”);
values.put(CalendarContract.Events.DURATION, duration); //”PT1H”
values.put(CalendarContract.Events.RRULE, rRule); //”FREQ=WEEKLY;COUNT=4”
Uri uri = cr.insert(CalendarContract.Events.CONTENT_URI, values);
long eventID = ContentUris.parseId(uri);
Brak komentarzy:
Prześlij komentarz