Descripción general del proveedor de calendario

El Proveedor de calendario es un repositorio de los eventos de calendario de un usuario. La API del Proveedor de calendario te permite consultar, insertar, actualizar y borrar operaciones en calendarios, eventos, asistentes, recordatorios, etcétera.

Tanto las aplicaciones como los adaptadores de sincronización pueden usar la API. Las reglas varían según el tipo de programa que realice las llamadas. Este documento se enfoca principalmente en el uso de la API del Proveedor de calendario como aplicación. Para obtener información sobre en qué se diferencian los adaptadores de sincronización, consulta Adaptadores de sincronización.

Por lo general, para leer o escribir datos en el calendario, el manifiesto de una aplicación debe incluir los permisos correspondientes, que se describen en Permisos de usuario. Para facilitar la realización de operaciones comunes, el Proveedor de calendario ofrece un conjunto de intents, como se describe en Intents de calendario. Estos intents llevan a los usuarios a la aplicación de Calendario para insertar, ver y editar eventos. El usuario interactúa con la aplicación de Calendario y, luego, regresa a la aplicación original. Por lo tanto, tu aplicación no necesita solicitar permisos ni proporcionar una interfaz de usuario para ver o crear eventos.

Conceptos básicos

Los proveedores de contenido almacenan datos y permiten que las aplicaciones accedan a ellos. Los proveedores de contenido que ofrece la plataforma de Android (incluido el Proveedor de Calendario) suelen exponer datos como un conjunto de tablas basadas en un modelo de base de datos relacional, en el que cada fila es un registro y cada columna son datos de un tipo y significado particular. A través de la API del Proveedor de calendario, las aplicaciones y los adaptadores de sincronización pueden obtener acceso de lectura/escritura a las tablas de la base de datos que contienen los datos del calendario de un usuario.

Cada proveedor de contenido expone un URI público (unido como un objeto Uri) que identifica su conjunto de datos de forma única. Un proveedor de contenido que controla varios conjuntos de datos (múltiples tablas) expone un URI independiente para cada uno. Todos los URI para proveedores comienzan con la string "content://". Esto indica que los datos son controlados por un proveedor de contenido. El Proveedor de calendario define constantes para los URI de cada una de sus clases (tablas). Estos URI tienen el formato <class>.CONTENT_URI. Por ejemplo, Events.CONTENT_URI.

La figura 1 muestra una representación gráfica del modelo de datos del Proveedor de calendario. Muestra las tablas principales y los campos que las vinculan entre sí.

Modelo de datos del proveedor de calendario

Figura 1: Modelo de datos Proveedor de calendario.

Un usuario puede tener varios calendarios, y es posible asociar diferentes calendarios con distintos tipos de cuenta (Calendario de Google, Exchange, etc.).

El CalendarContract define el modelo de datos para la información relacionada con calendarios y eventos. Esos datos se almacenan en una variedad de tablas que se indican a continuación.

Tabla (clase) Descripción

CalendarContract.Calendars

Esta tabla contiene información específica del calendario. Cada fila de la tabla contiene los detalles de un solo calendario, como el nombre, el color, la información de sincronización, etcétera.
CalendarContract.Events Esta tabla contiene información específica del evento. Cada fila de esta tabla contiene la información de un solo evento, por ejemplo, el título, la ubicación, la hora de inicio, la hora de finalización, etcétera. El evento puede ocurrir una sola vez o repetirse varias veces. Los asistentes, los recordatorios y las propiedades extendidas se almacenan en tablas separadas. Cada una tiene un EVENT_ID que hace referencia al _ID en la tabla Eventos.
CalendarContract.Instances Esta tabla contiene la hora de inicio y finalización de cada caso de un evento. Cada fila de esta tabla representa un solo caso de un evento. Para los eventos únicos, hay una asignación 1:1 de instancias a eventos. Para los eventos recurrentes, se generan automáticamente varias filas que corresponden a los distintos casos de ese evento.
CalendarContract.Attendees Esta tabla contiene la información del asistente al evento (invitado). Cada fila representa un solo invitado a un evento. Especifica el tipo de invitado y la respuesta de este a la invitación para el evento.
CalendarContract.Reminders Esta tabla contiene los datos de alerta/notificación. Cada fila representa una sola alerta para un evento. Un evento puede tener varios recordatorios. La cantidad máxima de recordatorios por evento se especifica en MAX_REMINDERS, que establece el adaptador de sincronización al que pertenece el calendario determinado. Los recordatorios se especifican en minutos antes del evento y tienen un método que determina cómo se alertará al usuario.

La API del Proveedor de calendario fue diseñada para ser flexible y eficaz. Al mismo tiempo, es importante proporcionar una buena experiencia del usuario final y proteger la integridad del calendario y sus datos. Con ese fin, ten en cuenta lo siguiente cuando uses la API:

  • Insertar, actualizar y ver eventos de calendario Para insertar, modificar y leer eventos directamente desde el Proveedor de Calendario, necesitas los permisos adecuados. Sin embargo, si no estás compilando una aplicación de calendario completa ni un adaptador de sincronización, no es necesario solicitar estos permisos. En su lugar, puedes usar intents compatibles con la aplicación Calendario de Android para entregar operaciones de lectura y escritura a esa aplicación. Cuando usas los intents, tu aplicación envía a los usuarios a la aplicación de Calendario para que realicen la operación deseada en un formulario precompletado. Una vez que finalicen, regresarán a tu aplicación. Cuando diseñas tu aplicación para que realice operaciones comunes mediante el Calendario, les proporcionas a los usuarios una interfaz de usuario coherente y sólida. Este es el enfoque recomendado. Para obtener más información, consulta Intents de calendario.
  • Adaptadores de sincronización. Un adaptador de sincronización sincroniza los datos del calendario que se encuentran en el dispositivo de un usuario con otro servidor o fuente de datos. En las tablas CalendarContract.Calendars y CalendarContract.Events, hay columnas que se reservan para que las usen los adaptadores de sincronización. Ni el proveedor ni las aplicaciones deben modificarlas. De hecho, no son visibles a menos que se acceda a ellos como un adaptador de sincronización. Para obtener más información sobre los adaptadores de sincronización, consulta Adaptadores de sincronización.

Permisos del usuario

Para leer datos del calendario, una aplicación debe incluir el permiso READ_CALENDAR en su archivo de manifiesto. Debe incluir el permiso WRITE_CALENDAR para borrar, insertar o actualizar datos del calendario:

<?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>

Tabla de calendarios

La tabla CalendarContract.Calendars contiene detalles de calendarios individuales. Las siguientes columnas de calendarios admiten la escritura de una aplicación y de un adaptador de sincronización. Para obtener una lista completa de los campos admitidos, consulta la referencia de CalendarContract.Calendars.

Constante Descripción
NAME El nombre del calendario.
CALENDAR_DISPLAY_NAME El nombre del calendario que se le muestra al usuario.
VISIBLE Un valor booleano que indica si se seleccionó el calendario para exhibirlo. El valor 0 indica que no se deben mostrar los eventos asociados con este calendario. El valor 1 indica que se deben mostrar los eventos asociados con ese calendario. Este valor afecta la generación de filas en la tabla CalendarContract.Instances.
SYNC_EVENTS Un valor booleano que indica si se debe sincronizar el calendario y si se deben almacenar sus eventos en el dispositivo. El valor 0 indica que no se debe sincronizar este calendario ni se deben almacenar sus eventos en el dispositivo. El valor 1 indica que los eventos de ese calendario deben sincronizarse y almacenarse en el dispositivo.

Incluir un tipo de cuenta para todas las operaciones

Si realizas una consulta en un Calendars.ACCOUNT_NAME, también debes incluir Calendars.ACCOUNT_TYPE en la selección. Esto se debe a que una cuenta determinada solo se considera única si posee su ACCOUNT_NAME y su ACCOUNT_TYPE. ACCOUNT_TYPE es la cadena correspondiente al autenticador de cuenta que se usó cuando se registró la cuenta con AccountManager. También hay un tipo especial de cuenta llamado ACCOUNT_TYPE_LOCAL para los calendarios que no están asociados con una cuenta de dispositivo. Las cuentas de ACCOUNT_TYPE_LOCAL no se sincronizan.

Consulta de un calendario

Aquí tienes un ejemplo que muestra cómo obtener los calendarios que le pertenecen a un usuario en particular. Por cuestiones de simplicidad, en este ejemplo, la operación de consulta se muestra en el subproceso de la interfaz de usuario ("subproceso principal"). En la práctica, esto se debe hacer en un subproceso asíncrono en lugar del subproceso principal. Para obtener más información, consulta Cargadores. Si no solo lees datos, sino que los modificas, consulta 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;

En la siguiente parte del ejemplo, construyes tu consulta. La selección especifica los criterios para la consulta. En este ejemplo, la consulta busca calendarios que tengan ACCOUNT_NAME "hera@example.com", "com.example" de ACCOUNT_TYPE y OWNER_ACCOUNT "hera@example.com". Si deseas ver todos los calendarios que vio un usuario, no solo los de su propiedad, omite la OWNER_ACCOUNT. La consulta muestra un objeto Cursor que puedes usar para recorrer el conjunto de resultados que muestra la consulta de la base de datos. Para obtener más información sobre el uso de consultas en proveedores de contenido, visita Proveedores de contenido.

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);

La siguiente sección usa el cursor para recorrer el conjunto de resultados. Usa las constantes que se configuraron al comienzo del ejemplo para mostrar los valores de cada campo.

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...

   ...
}

Modificación de un calendario

Para actualizar un calendario, puedes proporcionar el _ID del calendario como un ID agregado al URI (withAppendedId()) o como el primer elemento de selección. La selección debe comenzar con "_id=?", y el primer selectionArg debe ser el _ID del calendario. También puedes realizar actualizaciones codificando el ID en el URI. En este ejemplo, se cambia el nombre visible de un calendario mediante el enfoque (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);

Insertar un calendario

Los calendarios están diseñados para ser administrados principalmente por un adaptador de sincronización, por lo que solo debes insertar calendarios nuevos como un adaptador de sincronización. En general, las aplicaciones solo pueden realizar cambios superficiales en los calendarios, como cambiar el nombre visible. Si una aplicación necesita crear un calendario local, puede hacerlo mediante la inserción del calendario como un adaptador de sincronización mediante un ACCOUNT_TYPE de ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCAL es un tipo de cuenta especial para calendarios que no están asociados a una cuenta de dispositivo. Los calendarios de este tipo no se sincronizan con un servidor. Para ver más información sobre los adaptadores de sincronización, consulta Adaptadores de sincronización.

Tabla de eventos

La tabla CalendarContract.Events contiene detalles de eventos individuales. Para agregar, actualizar o borrar eventos, una aplicación debe incluir el permiso WRITE_CALENDAR en su archivo de manifiesto.

Las siguientes columnas de eventos admiten la escritura de una aplicación y un adaptador de sincronización. Para obtener una lista completa de los campos admitidos, consulta la referencia de CalendarContract.Events.

Constante Descripción
CALENDAR_ID El _ID del calendario al que pertenece el evento.
ORGANIZER Correo electrónico del organizador (propietario) del evento.
TITLE Título del evento.
EVENT_LOCATION Lugar donde se realiza el evento.
DESCRIPTION Descripción del evento.
DTSTART Hora de inicio del evento en milisegundos de UTC desde el epoch.
DTEND Hora de finalización del evento en milisegundos de UTC desde el epoch.
EVENT_TIMEZONE Zona horaria del evento.
EVENT_END_TIMEZONE Zona horaria para la hora de finalización del evento.
DURATION La duración del evento en formato RFC5545. Por ejemplo, un valor de "PT1H" indica que el evento debe durar una hora, y un valor de "P2W" indica una duración de 2 semanas.
ALL_DAY El valor 1 indica que el evento transcurre durante un día completo, como lo define la zona horaria local. El valor igual a 0 indica que es un evento normal que puede comenzar y finalizar a cualquier hora durante un día.
RRULE La regla de recurrencia para el formato del evento. Por ejemplo, "FREQ=WEEKLY;COUNT=10;WKST=SU". Puedes encontrar más ejemplos aquí.
RDATE Las fechas de recurrencia del evento. Por lo general, usas RDATE junto con RRULE para definir un conjunto agregado de casos repetidos. Para obtener más información, consulta la especificación de RFC5545.
AVAILABILITY Indica si este evento cuenta como tiempo ocupado o tiempo libre que se puede reprogramar.
GUESTS_CAN_MODIFY Indica si los invitados pueden modificar el evento.
GUESTS_CAN_INVITE_OTHERS Indica si los invitados pueden invitar a otras personas.
GUESTS_CAN_SEE_GUESTS Indica si los invitados pueden ver la lista de asistentes.

Agregar eventos

Cuando tu aplicación inserta un evento nuevo, te recomendamos que uses un intent INSERT, como se describe en Cómo usar un intent para insertar un evento. Sin embargo, si lo necesitas, puedes insertar eventos directamente. En esta sección, se describe cómo hacerlo.

Aquí te damos las reglas para insertar un nuevo evento:

Aquí te mostramos un ejemplo de cómo insertar un evento. Lo haremos en el subproceso de IU para que sea más simple. En la práctica, las inserciones y actualizaciones deben realizarse en un subproceso asíncrono para trasladar la acción a un subproceso en segundo plano. Para obtener más información, consulta 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
//
//

Nota: Observa cómo este ejemplo captura el ID del evento después de que se crea el evento. Esta es la forma más fácil de capturar el ID de un evento. A menudo, necesitas el ID del evento para realizar otras operaciones en el calendario, por ejemplo, para agregar asistentes o recordatorios a un evento.

Actualizar eventos

Si tu aplicación quiere permitir que el usuario edite un evento, te recomendamos que uses un intent EDIT, como se describe en Cómo usar un intent para editar un evento. Sin embargo, si lo necesitas, puedes editar eventos directamente. Para actualizar un evento, puedes proporcionar el _ID de este como un ID agregado al URI (withAppendedId()) o como el primer elemento de la selección. La selección debe comenzar con "_id=?", y el primer selectionArg debe ser el _ID del evento. También puedes realizar actualizaciones con una selección sin ID. Aquí hay un ejemplo de cómo actualizar un evento. Cambia el título del evento con el enfoque 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);

Borrar eventos

Puedes borrar un evento por su _ID como un ID adjunto en el URI o mediante la selección estándar. Si usas un ID adjunto, tampoco podrás realizar una selección. Existen dos versiones de eliminación: como aplicación y como adaptador de sincronización. Cuando se borra una aplicación, se establece la columna deleted a 1. Esta marca le indica al adaptador de sincronización que se borró la fila y que esa eliminación debe propagarse al servidor. La eliminación del adaptador de sincronización quita el evento de la base de datos junto con todos sus datos asociados. A continuación, se muestra un ejemplo de una aplicación que borra un evento a través de su _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);

Tabla de asistentes

Cada fila de la tabla CalendarContract.Attendees representa a un solo asistente o invitado a un evento. Llamar a query() muestra una lista de asistentes al evento con el EVENT_ID especificado. Este EVENT_ID debe coincidir con el _ID de un evento en particular.

En la siguiente tabla, se enumeran los campos que admiten escritura. Cuando insertes un asistente nuevo, debes incluirlos a todos, excepto ATTENDEE_NAME.

Constante Descripción
EVENT_ID El ID del evento.
ATTENDEE_NAME El nombre del asistente.
ATTENDEE_EMAIL La dirección de correo electrónico del asistente.
ATTENDEE_RELATIONSHIP

La relación del asistente con el evento. Una de las siguientes opciones:

ATTENDEE_TYPE

El tipo de asistente. Una de las siguientes opciones:

ATTENDEE_STATUS

El estado de asistencia del asistente. Una de las siguientes opciones:

Agregar asistentes

Aquí te mostramos un ejemplo de cómo agregar un asistente a un evento. Ten en cuenta que se requiere el EVENT_ID:

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);

Tabla de recordatorios

Cada fila de la tabla CalendarContract.Reminders representa un solo recordatorio para un evento. Llamar a query() muestra una lista de recordatorios para el evento con el EVENT_ID especificado.

La siguiente tabla incluye los campos de los recordatorios que admiten escritura. Se deben incluir todos cuando se inserta un nuevo recordatorio. Ten en cuenta que los adaptadores de sincronización especifican los tipos de recordatorios que admiten en la tabla CalendarContract.Calendars. Consulta ALLOWED_REMINDERS para obtener más información.

Constante Descripción
EVENT_ID El ID del evento.
MINUTES Cuántos minutos antes del evento se debe enviar el recordatorio.
METHOD

El método de alarma, tal como esté configurado en el servidor. Una de las siguientes opciones:

Agregar recordatorios

Este ejemplo agrega un recordatorio a un evento. El recordatorio se envía 15 minutos antes del evento.

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);

Tabla de instancias

La tabla CalendarContract.Instances contiene la hora de inicio y finalización de los casos de un evento. Cada fila de esta tabla representa un solo caso de un evento. La tabla de instancias no admite escritura y solo proporciona una forma de consultar casos del evento.

La siguiente tabla incluye algunos de los campos que puedes consultar para una instancia. Ten en cuenta que la zona horaria se define mediante KEY_TIMEZONE_TYPE y KEY_TIMEZONE_INSTANCES.

Constante Descripción
BEGIN El horario de inicio de la instancia en milisegundos (UTC).
END El horario de finalización de la instancia en milisegundos (UTC).
END_DAY El día de finalización de la instancia según el calendario juliano, en relación con la zona horaria del calendario.
END_MINUTE El minuto final de la instancia medido desde la medianoche, en la zona horaria del calendario.
EVENT_ID El _ID del evento para esta instancia.
START_DAY El día de inicio de la instancia, según el calendario juliano, según la zona horaria del calendario.
START_MINUTE El minuto inicial de la instancia medido desde la medianoche, de acuerdo con la zona horaria del calendario.

Consulta de la tabla de instancias

Para consultar la tabla de instancias, debes especificar un intervalo de tiempo para la consulta en el URI. En este ejemplo, CalendarContract.Instances obtiene acceso al campo TITLE mediante la implementación de la interfaz CalendarContract.EventsColumns. En otras palabras, TITLE se muestra a través de una vista de base de datos, no a través de la consulta de la tabla CalendarContract.Instances sin procesar.

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()));
    }
 }

Intents de calendario

Tu aplicación no necesita permisos para leer y escribir datos del calendario. En su lugar, puede usar intents compatibles con la aplicación Calendario de Android para entregar operaciones de lectura y escritura a esa aplicación. La siguiente tabla incluye las intents admitidas por el proveedor de calendario:

Acción URI Descripción Adicionales

VIEW

content://com.android.calendar/time/<ms_since_epoch>

También puedes consultar el URI con CalendarContract.CONTENT_URI. Si deseas obtener un ejemplo de cómo usar este intent, consulta Cómo usar intents para ver datos del calendario.
Abrir el calendario en la hora especificada por <ms_since_epoch>. Ningún contenido de este tipo

VIEW

content://com.android.calendar/events/<event_id>

También puedes consultar el URI con Events.CONTENT_URI. Si deseas obtener un ejemplo de cómo usar este intent, consulta Cómo usar intents para ver datos del calendario.
Ver el evento especificado por <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

content://com.android.calendar/events/<event_id>

También puedes consultar el URI con Events.CONTENT_URI. Si deseas ver un ejemplo de cómo usar este intent, consulta Cómo usar un intent para editar un evento.
Edita el evento especificado por <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

También puedes consultar el URI con Events.CONTENT_URI. Para ver un ejemplo de cómo usar este intent, consulta Cómo usar un intent para insertar un evento.
Crea un evento. Cualquiera de los extras que se indican en la tabla a continuación.

La siguiente tabla incluye los extras de intents admitidos por el proveedor de calendario:

Extra de intents Descripción
Events.TITLE El nombre del evento.
CalendarContract.EXTRA_EVENT_BEGIN_TIME El horario de inicio del evento en milisegundos desde el epoch.
CalendarContract.EXTRA_EVENT_END_TIME El horario de finalización del evento en milisegundos desde el epoch.
CalendarContract.EXTRA_EVENT_ALL_DAY Valor booleano que indica que un evento transcurre durante todo el día. El valor puede ser true o false.
Events.EVENT_LOCATION El lugar del evento.
Events.DESCRIPTION La descripción del evento.
Intent.EXTRA_EMAIL Las direcciones de correo electrónico de las personas a las que se invitará, en forma de lista separada por comas.
Events.RRULE La regla de recurrencia para el evento.
Events.ACCESS_LEVEL Indica si el evento es privado o público.
Events.AVAILABILITY Indica si este evento cuenta como tiempo ocupado o tiempo libre que puede reprogramarse.

Las siguientes secciones describen cómo usar estas intents.

Cómo usar una intent para insertar un evento

El uso del intent INSERT permite que tu aplicación entregue la tarea de inserción del evento al Calendario. Con este enfoque, tu aplicación ni siquiera necesita tener el permiso WRITE_CALENDAR incluido en su archivo de manifiesto.

Cuando los usuarios ejecutan una aplicación que usa este enfoque, la aplicación los envía al Calendario para que terminen de agregar el evento. El intent INSERT usa campos adicionales para prepropagar un formulario con los detalles del evento en el Calendario. Luego, los usuarios pueden cancelar el evento, editar el formulario según sea necesario o guardar el evento en sus calendarios.

Este es un fragmento de código que programa un evento para el 19 de enero de 2012, que se ejecutará desde las 7:30 a.m. hasta las 8:30 a.m. Ten en cuenta lo siguiente sobre este fragmento de código:

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);

Cómo usar una intent para editar un evento

Puedes actualizar un evento directamente, como se describe en Cómo actualizar eventos. Sin embargo, si usas el intent EDIT, una aplicación que no tiene permiso podrá transferir la edición del evento a la aplicación de Calendario. Cuando los usuarios terminen de editar su evento en Calendario, regresarán a la aplicación original.

A continuación, se muestra un ejemplo de un intent que establece un título nuevo para un evento especificado y permite a los usuarios editar el evento en el Calendario.

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);

Cómo usar intents para ver datos del calendario

El Proveedor de calendario ofrece dos formas diferentes de usar el intent VIEW:

  • Para abrir el calendario en una fecha determinada.
  • Para ver un evento.

Este es un ejemplo que muestra cómo abrir el calendario en una fecha determinada:

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);

Este es un ejemplo que muestra cómo abrir un evento para visualizarlo:

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);

Adaptadores de sincronización

Existen solo pequeñas diferencias en cómo una aplicación y un adaptador de sincronización acceden al proveedor de calendario:

  • Un adaptador de sincronización debe especificar que es un adaptador de sincronización mediante la configuración de CALLER_IS_SYNCADAPTER en true.
  • Un adaptador de sincronización debe proporcionar un ACCOUNT_NAME y un ACCOUNT_TYPE como parámetros de consulta en el URI.
  • Un adaptador de sincronización tiene acceso de escritura a más columnas que una aplicación o un widget. Por ejemplo, una aplicación solo puede modificar algunas características de un calendario, como el nombre, el nombre visible, la configuración de visibilidad y si el calendario está sincronizado. En comparación, un adaptador de sincronización puede acceder no solo a esas columnas, sino a muchas otras, como el color del calendario, la zona horaria, el nivel de acceso, la ubicación, etcétera. Sin embargo, el adaptador de sincronización está restringido a los ACCOUNT_NAME y ACCOUNT_TYPE que especificó.

Aquí te mostramos un método de ayuda que puedes usar para mostrar un URI para usar con un adaptador de sincronización:

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();
 }