Интеграция Calendar Provider (системный календарь) в Android-приложение

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Интеграция Calendar Provider (системный календарь) в Android-приложение
Простая
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Интеграция Calendar Provider (системный календарь) в Android-приложение

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

Разрешения

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

Чтение событий

События хранятся в таблице 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)

Это проще, безопаснее и не требует разрешений. Подходит для большинства случаев, когда приложение не ведёт собственный список событий.

Интеграция CalendarProvider занимает 1-3 дня в зависимости от объёма функциональности: чтение событий, создание, синхронизация нескольких аккаунтов. Стоимость рассчитывается индивидуально.