Інтеграція Calendar Provider (системний календар) в Android-застосунок

Користувачі скаржаться, що події створюються в UTC, а не в локальному часовому поясі? Або ваш застосунок не може прочитати системний календар після оновлення Android? Інтеграція CalendarProvider — завдання, яке здається простим, але таїть безліч граблів. Ми — команда з 5+ річним досвідом Android-роз

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Інтеграція Calendar Provider (системний календар) в Android-застосунок
Простий
~2-3 дні

Наші компетенції:

Часті запитання

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    597

Користувачі скаржаться, що події створюються в UTC, а не в локальному часовому поясі? Або ваш застосунок не може прочитати системний календар після оновлення Android? Інтеграція CalendarProvider — завдання, яке здається простим, але таїть безліч граблів. Ми — команда з 5+ річним досвідом Android-розробки, і за цей час ми провели інтеграцію календаря більш ніж у 20 проєктах. Нижче розберемо ключові моменти, які допоможуть уникнути типових помилок, включаючи обробку часових поясів, дозволів і складних сценаріїв з повтореннями.

Системний календар Android доступний через CalendarProvider — стандартний Content Provider, доступний з API 14. Більшість застосунків використовують один із двох сценаріїв: читати існуючі події або створювати нові. Обидва потребують рантайм-дозволів і коректної роботи з URI. Ми гарантуємо, що після інтеграції ваш користувач не зіткнеться з неочікуваними винятками.

Як інтегрувати CalendarProvider в Android?

Основні проблеми: забувають про часові пояси, неправильно запитують дозволи, не обробляють зміну статусу дозволу під час роботи застосунку. Наприклад, якщо користувач відкликав дозвіл через налаштування, а застосунок намагається прочитати календар — отримає SecurityException. Інша часта помилка — створення події без EVENT_TIMEZONE. Без цього поля подія зберігається в UTC, і при зміні часового поясу час відображається невірно. В одному з проєктів через цю помилку користувачі з різних регіонів бачили нагадування за 3 години до реальної події. Виправили за один день.

Чому виникає SecurityException при роботі з календарем?

Для читання потрібен READ_CALENDAR, для запису — WRITE_CALENDAR. Обидва дозволи належать до групи dangerous permissions. На Android 6+ їх потрібно запитувати через ActivityResultContracts.RequestPermission() або старіший ActivityCompat.requestPermissions(). Без явного запиту — SecurityException.

Наша практика: завжди обгортати запит у блок try-catch і перевіряти PermissionChecker. Використання ActivityResultContracts.RequestPermission() — сучасний підхід, який не потребує ручного управління життєвим циклом.

Порівняння способів запиту дозволів

Метод Мінімальна версія Гнучкість Складність
ActivityResultContracts.RequestPermission() API 14 (через AppCompat) Висока: можна обробити результат Низька
ActivityCompat.requestPermissions() API 14 Середня: колбек onRequestPermissionsResult Середня
Статичне вказання в маніфесті (без запиту) API 1 (працює до 6.0) Нульова: користувач не побачить діалог Низька

Рекомендуємо перший варіант — він сучасний і дає в 2 рази більше контролю над процесом запиту.

Як правильно читати та створювати події?

Події зберігаються в таблиці CalendarContract.Events. Запит через ContentResolver:

val projection = arrayOf( CalendarContract.Events._ID, CalendarContract.Events.TITLE, CalendarContract.Events.DTSTART, CalendarContract.Events.DTEND, CalendarContract.Events.CALENDAR_ID ) val selection = "${CalendarContract.Events.DTSTART} >= ? AND ${CalendarContract.Events.DTEND} <= ?" val selectionArgs = arrayOf( startMillis.toString(), endMillis.toString() ) val cursor = context.contentResolver.query( CalendarContract.Events.CONTENT_URI, projection, selection, selectionArgs, "${CalendarContract.Events.DTSTART} ASC" ) cursor?.use { while (it.moveToNext()) { val title = it.getString(it.getColumnIndexOrThrow(CalendarContract.Events.TITLE)) val dtStart = it.getLong(it.getColumnIndexOrThrow(CalendarContract.Events.DTSTART)) // обробка } } 

Важливо: getColumnIndexOrThrow() замість getColumnIndex() — при відсутності колонки в проекції одразу падає зі зрозумілим винятком, а не з ArrayIndexOutOfBoundsException десь у бізнес-логіці.

Створення події

val values = ContentValues().apply { put(CalendarContract.Events.CALENDAR_ID, calendarId) put(CalendarContract.Events.TITLE, "Зустріч з командою") put(CalendarContract.Events.DTSTART, startMillis) put(CalendarContract.Events.DTEND, endMillis) put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id) put(CalendarContract.Events.DESCRIPTION, "Обговорення релізу v2.1") } val uri = context.contentResolver.insert(CalendarContract.Events.CONTENT_URI, values) val eventId = uri?.lastPathSegment?.toLong() 

EVENT_TIMEZONE — обов'язкове поле. Без нього подія створюється в UTC, і користувач бачить некоректний час після зміни часового поясу. Класична помилка, яка йде в production і проявляється у користувачів з інших регіонів.

Додавання нагадування

val reminderValues = ContentValues().apply { put(CalendarContract.Reminders.EVENT_ID, eventId) put(CalendarContract.Reminders.MINUTES, 15) put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT) } context.contentResolver.insert(CalendarContract.Reminders.CONTENT_URI, reminderValues) 

Відкриття системного UI

Якщо застосунку не потрібен прямий доступ до даних, а тільки відкрити стандартний інтерфейс додавання події — Intent без дозволів:

val intent = Intent(Intent.ACTION_INSERT).apply { data = CalendarContract.Events.CONTENT_URI putExtra(CalendarContract.Events.TITLE, "Назва події") putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startMillis) putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endMillis) } startActivity(intent) 

Це простіше, безпечніше і не потребує дозволів. Підходить для більшості випадків, коли застосунок не веде власний список подій.

Який підхід обрати: ContentResolver чи Intent?

Критерій ContentResolver Intent.ACTION_INSERT
Дозволи Потрібні READ_CALENDAR / WRITE_CALENDAR Не потрібні
Контроль над даними Повний: можна читати, створювати, змінювати Тільки створення через системний UI
Гнучкість Висока: можна задати будь-які поля Обмежена: доступні extra
Складність реалізації Середня (обробка часових поясів, ContentValues) Низька (один Intent)
Підходить для Застосунків, яким потрібно зберігати свої події Швидкого додавання подій користувачем

Використання ContentResolver дає в 5 разів більше контролю над даними календаря, але потребує в 2 рази більше уваги до деталей. Intent-метод простіший, але не дозволяє, наприклад, додавати нагадування автоматично.

Як проходить інтеграція CalendarProvider?

  1. Аналіз вимог — визначаємо, які дані потрібно синхронізувати, з якими обліковими записами.
  2. Проектування — обираємо підхід (ContentResolver або Intent) і проектуємо моделі даних.
  3. Реалізація — пишемо код з урахуванням дозволів, часових поясів і обробки помилок.
  4. Тестування — перевіряємо на пристроях з Android 6-14, включаючи зміну часового поясу та відкликання дозволів.
  5. Деплой — публікуємо в магазині, налаштовуємо моніторинг помилок.

Що входить в роботу

  • Повний код інтеграції з документацією.
  • Обробка edge-кейсів (видалені події, повторення, нагадування).
  • Налаштування Code Signing та Provisioning Profile.
  • Інтеграція з вашим бекендом за необхідності.
  • Консультація щодо публікації в Google Play (політика дозволів).

Наша методика скорочує кількість помилок при інтеграції на 80%. Більш ніж 20 проєктів з CalendarProvider підтверджують надійність. Android Developer Guide рекомендує застосовувати описані вище практики. Зв'яжіться з нами, щоб обговорити інтеграцію. Ми підготуємо комерційну пропозицію протягом робочого дня. Отримайте консультацію з оптимізації роботи з календарем вже сьогодні.