Calendar Provider는 사용자의 캘린더 일정 저장소입니다. Calendar Provider API를 사용하면 캘린더, 일정, 참석자, 리마인더와 같은 항목에 대해 쿼리, 삽입, 업데이트, 삭제 작업을 실행할 수 있습니다.
Calendar Provider API는 애플리케이션과 동기화 어댑터에서 사용할 수 있습니다. 규칙은 호출을 수행하는 프로그램 유형에 따라 다릅니다. 이 문서에서는 Calendar Provider API를 애플리케이션으로 사용하는 데 중점을 둡니다. 동기화 어댑터가 어떻게 다른지에 관한 설명은 동기화 어댑터를 참고하세요.
일반적으로 캘린더 데이터를 읽거나 쓰려면 애플리케이션의 매니페스트에 사용자 권한에 설명된 적절한 권한이 포함되어야 합니다. 일반적인 작업을 더 쉽게 수행할 수 있도록 캘린더 제공자는 캘린더 인텐트에 설명된 대로 인텐트 집합을 제공합니다. 이러한 인텐트는 사용자를 Calendar 애플리케이션으로 이동시켜 일정을 삽입, 조회, 수정할 수 있게 합니다. 사용자는 Calendar 애플리케이션과 상호작용한 후 원래 애플리케이션으로 돌아갑니다. 따라서 애플리케이션에서 권한을 요청할 필요가 없으며 이벤트를 보거나 만들기 위한 사용자 인터페이스를 제공할 필요도 없습니다.
기본사항
콘텐츠 제공자는 데이터를 저장하고 애플리케이션에서 액세스할 수 있도록 합니다. Android 플랫폼에서 제공하는 콘텐츠 제공자 (Calendar Provider 포함)는 일반적으로 관계형 데이터베이스 모델을 기반으로 하는 테이블 집합으로 데이터를 노출합니다. 여기서 각 행은 레코드이고 각 열은 특정 유형과 의미의 데이터입니다. Calendar Provider API를 통해 애플리케이션과 동기화 어댑터는 사용자의 캘린더 데이터를 보유하는 데이터베이스 테이블에 대한 읽기/쓰기 액세스 권한을 얻을 수 있습니다.
모든 콘텐츠 제공자는 데이터 세트를 고유하게 식별하는 공개 URI(Uri 객체로 래핑됨)를 노출합니다. 여러 데이터 세트 (여러 테이블)를 제어하는 콘텐츠 제공자는 각 데이터 세트에 대해 별도의 URI를 노출합니다. 제공자의 모든 URI는 문자열 "content://"로 시작합니다. 이렇게 하면 데이터가 콘텐츠 제공자에 의해 제어되는 것으로 식별됩니다. 캘린더 제공자는 각 클래스 (테이블)의 URI에 대한 상수를 정의합니다. 이러한 URI의 형식은 <class>.CONTENT_URI입니다. 예를 들면 Events.CONTENT_URI입니다.
그림 1은 Calendar Provider 데이터 모델을 그래픽으로 표현한 것입니다. 기본 테이블과 테이블을 서로 연결하는 필드가 표시됩니다.

사용자는 여러 캘린더를 가질 수 있으며, 서로 다른 캘린더를 서로 다른 유형의 계정 (Google Calendar, Exchange 등)과 연결할 수 있습니다.
CalendarContract는 캘린더 및 일정 관련 정보의 데이터 모델을 정의합니다. 이 데이터는 다음과 같은 여러 테이블에 저장됩니다.
| 테이블 (클래스) | 설명 |
|---|---|
| 이 테이블에는 캘린더 관련 정보가 포함됩니다. 이 테이블의 각 행에는 이름, 색상, 동기화 정보와 같은 단일 캘린더의 세부정보가 포함됩니다. | |
CalendarContract.Events |
이 테이블에는 이벤트별 정보가 포함됩니다. 이 테이블의 각 행에는 단일 이벤트에 대한 정보(예: 이벤트 제목, 위치, 시작 시간, 종료 시간)가 있습니다. 이벤트는 한 번 발생할 수도 있고 여러 번 반복될 수도 있습니다. 참석자, 알림, 확장 속성은 별도의 테이블에 저장됩니다.
각각 이벤트 테이블의 _ID를 참조하는 EVENT_ID가 있습니다. |
CalendarContract.Instances |
이 테이블에는 각 이벤트 발생의 시작 및 종료 시간이 포함됩니다. 이 테이블의 각 행은 단일 이벤트 발생을 나타냅니다 일회성 이벤트의 경우 인스턴스와 이벤트가 1:1로 매핑됩니다. 반복 일정의 경우 해당 일정의 여러 발생에 해당하는 여러 행이 자동으로 생성됩니다. |
CalendarContract.Attendees |
이 테이블에는 일정 참석자 (게스트) 정보가 포함됩니다. 각 행은 일정의 단일 참석자를 나타냅니다. 참석자 유형과 이벤트에 대한 참석자의 참석 여부 응답을 지정합니다. |
CalendarContract.Reminders |
이 테이블에는 알림 데이터가 있습니다. 각 행은 이벤트에 대한 단일 알림을 나타냅니다. 일정에는 여러 개의 알림이 있을 수 있습니다. 이벤트당 최대 알림 수는 MAX_REMINDERS에 지정되며, 이는 지정된 캘린더를 소유한 동기화 어댑터에 의해 설정됩니다. 알림은 일정 전 분 단위로 지정되며 사용자에게 알림을 보내는 방법을 결정하는 메서드가 있습니다. |
Calendar Provider API는 유연하고 강력하게 설계되었습니다. 동시에 우수한 최종 사용자 환경을 제공하고 캘린더 및 데이터의 무결성을 보호하는 것이 중요합니다. 이를 위해 API를 사용할 때 유의해야 할 사항은 다음과 같습니다.
- 캘린더 일정 삽입, 업데이트, 보기 캘린더 제공자에서 일정을 직접 삽입, 수정, 읽기 위해서는 적절한 권한이 필요합니다. 하지만 완전한 캘린더 애플리케이션이나 동기화 어댑터를 빌드하지 않는다면 이러한 권한을 요청할 필요가 없습니다. 대신 Android의 Calendar 애플리케이션에서 지원하는 인텐트를 사용하여 읽기 및 쓰기 작업을 해당 애플리케이션에 전달할 수 있습니다. 인텐트를 사용하면 애플리케이션은 사용자를 Calendar 애플리케이션으로 보내 미리 작성된 양식에서 선택한 작업을 실행합니다. 완료되면 애플리케이션으로 돌아갑니다. Calendar를 통해 일반적인 작업을 실행하도록 애플리케이션을 설계하면 사용자에게 일관되고 강력한 사용자 인터페이스를 제공할 수 있습니다. 이것이 권장되는 방법입니다. 자세한 내용은 캘린더 인텐트를 참고하세요.
- 동기화 어댑터 동기화 어댑터는 사용자 기기의 캘린더 데이터를 다른 서버 또는 데이터 소스와 동기화합니다.
CalendarContract.Calendars및CalendarContract.Events테이블에는 동기화 어댑터가 사용하도록 예약된 열이 있습니다. 제공업체와 애플리케이션은 이를 수정해서는 안 됩니다. 실제로 동기화 어댑터로 액세스하지 않는 한 표시되지 않습니다. 동기화 어댑터에 관한 자세한 내용은 동기화 어댑터를 참고하세요.
사용자 권한
캘린더 데이터를 읽으려면 애플리케이션의 매니페스트 파일에 READ_CALENDAR 권한이 포함되어야 합니다. 캘린더 데이터를 삭제, 삽입 또는 업데이트하려면 WRITE_CALENDAR 권한을 포함해야 합니다.
<?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>
캘린더 테이블
CalendarContract.Calendars 테이블에는 개별 캘린더의 세부정보가 포함됩니다. 다음 Calendars 열은 애플리케이션과 동기화 어댑터 모두에서 쓸 수 있습니다.
지원되는 필드의 전체 목록은 CalendarContract.Calendars 참조를 확인하세요.
| 상수 | 설명 |
|---|---|
NAME |
캘린더의 이름입니다. |
CALENDAR_DISPLAY_NAME |
사용자에게 표시되는 이 캘린더의 이름입니다. |
VISIBLE |
캘린더가 표시되도록 선택되었는지 여부를 나타내는 불리언입니다. 값이 0이면 이 캘린더와 연결된 일정이 표시되지 않아야 함을 나타냅니다. 값이 1이면 이 캘린더와 연결된 일정이 표시되어야 함을 나타냅니다. 이 값은 CalendarContract.Instances 테이블의 행 생성에 영향을 미칩니다. |
SYNC_EVENTS |
캘린더를 동기화하고 일정을 기기에 저장해야 하는지 여부를 나타내는 불리언입니다. 값이 0이면 이 캘린더를 동기화하지 않거나 기기에 일정을 저장하지 않습니다. 값이 1이면 이 캘린더의 일정을 동기화하고 기기에 일정을 저장합니다. |
모든 작업에 계정 유형 포함
Calendars.ACCOUNT_NAME를 쿼리하는 경우 선택 항목에 Calendars.ACCOUNT_TYPE도 포함해야 합니다. 이는 특정 계정이 ACCOUNT_NAME 및 ACCOUNT_TYPE 모두를 고려할 때만 고유한 것으로 간주되기 때문입니다. ACCOUNT_TYPE은 계정이 AccountManager에 등록될 때 사용된 계정 인증자에 해당하는 문자열입니다. 기기 계정과 연결되지 않은 캘린더를 위한 ACCOUNT_TYPE_LOCAL라는 특수한 유형의 계정도 있습니다.
ACCOUNT_TYPE_LOCAL 계정은 동기화되지 않습니다.
캘린더 쿼리
다음은 특정 사용자가 소유한 캘린더를 가져오는 방법을 보여주는 예입니다. 간단히 설명하기 위해 이 예에서는 사용자 인터페이스 스레드 ('기본 스레드')에 쿼리 작업이 표시됩니다. 실제로는 기본 스레드가 아닌 비동기 스레드에서 이 작업을 실행해야 합니다. 자세한 내용은 로더를 참고하세요. 데이터를 읽는 것뿐만 아니라 수정하는 경우 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
자바
// 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;
예시의 다음 부분에서는 쿼리를 구성합니다. 선택사항은 쿼리 기준을 지정합니다. 이 예시에서 쿼리는 ACCOUNT_NAME 'hera@example.com', ACCOUNT_TYPE 'com.example', OWNER_ACCOUNT 'hera@example.com'이 있는 캘린더를 찾습니다. 사용자가 소유한 캘린더뿐만 아니라 사용자가 조회한 모든 캘린더를 보려면 OWNER_ACCOUNT를 생략합니다.
쿼리는 데이터베이스 쿼리에서 반환된 결과 집합을 순회하는 데 사용할 수 있는 Cursor 객체를 반환합니다. 콘텐츠 제공자에서 쿼리를 사용하는 방법에 관한 자세한 내용은 콘텐츠 제공자를 참고하세요.
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)
자바
// 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);
다음 섹션에서는 커서를 사용하여 결과 집합을 단계별로 살펴봅니다. 예시의 시작 부분에서 설정된 상수를 사용하여 각 필드의 값을 반환합니다.
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... }
자바
// 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... ... }
캘린더 수정
캘린더를 업데이트하려면 캘린더의 _ID를 URI에 추가된 ID(withAppendedId()) 또는 첫 번째 선택 항목으로 제공하면 됩니다. 선택은 "_id=?"로 시작해야 하며 첫 번째 selectionArg는 캘린더의 _ID여야 합니다.
URI에서 ID를 인코딩하여 업데이트할 수도 있습니다. 이 예시에서는 (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")
자바
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);
캘린더 삽입하기
캘린더는 주로 동기화 어댑터에 의해 관리되도록 설계되었으므로 새 캘린더는 동기화 어댑터로만 삽입해야 합니다. 대부분의 경우 애플리케이션은 표시 이름 변경과 같은 캘린더의 피상적인 변경만 수행할 수 있습니다. 애플리케이션이 로컬 캘린더를 만들어야 하는 경우 ACCOUNT_TYPE_LOCAL의 ACCOUNT_TYPE를 사용하여 캘린더 삽입을 동기화 어댑터로 실행하면 됩니다.
ACCOUNT_TYPE_LOCAL는 기기 계정과 연결되지 않은 캘린더의 특별한 계정 유형입니다. 이 유형의 캘린더는 서버에 동기화되지 않습니다. 동기화 어댑터에 관한 자세한 내용은 동기화 어댑터를 참고하세요.
이벤트 테이블
CalendarContract.Events 테이블에는 개별 이벤트의 세부정보가 포함됩니다. 일정을 추가, 업데이트 또는 삭제하려면 애플리케이션이 매니페스트 파일에 WRITE_CALENDAR 권한을 포함해야 합니다.
다음 이벤트 열은 애플리케이션과 동기화 어댑터 모두에서 쓸 수 있습니다. 지원되는 필드의 전체 목록은 CalendarContract.Events 참조를 확인하세요.
| 상수 | 설명 |
|---|---|
CALENDAR_ID |
일정이 속한 캘린더의 _ID입니다. |
ORGANIZER |
이벤트 주최자 (소유자)의 이메일입니다. |
TITLE |
일정 제목입니다. |
EVENT_LOCATION |
이벤트가 진행되는 장소입니다. |
DESCRIPTION |
이벤트에 대한 설명입니다. |
DTSTART |
에포크 이후 UTC 밀리초 단위로 이벤트가 시작되는 시간입니다. |
DTEND |
에포크 이후 UTC 밀리초 단위의 이벤트 종료 시간입니다. |
EVENT_TIMEZONE |
이벤트의 시간대입니다. |
EVENT_END_TIMEZONE |
일정 종료 시간의 시간대입니다. |
DURATION |
RFC5545 형식의 이벤트 기간입니다.
예를 들어 "PT1H" 값은 이벤트가 1시간 동안 지속되어야 함을 나타내고 "P2W" 값은 2주 동안 지속됨을 나타냅니다. |
ALL_DAY |
값이 1이면 이 이벤트가 현지 시간대로 정의된 하루 전체를 차지함을 나타냅니다. 값이 0이면 하루 중 언제든지 시작하고 종료할 수 있는 일반 이벤트임을 나타냅니다. |
RRULE |
일정 형식의 반복 규칙입니다. 예: "FREQ=WEEKLY;COUNT=10;WKST=SU". 더 많은 예시는 RFC5545 사양을 참고하세요. |
RDATE |
이벤트의 반복 날짜입니다.
일반적으로 RDATE를 RRULE와 함께 사용하여 반복되는 항목의 집계 집합을 정의합니다. 자세한 내용은 RFC5545 사양을 참고하세요. |
AVAILABILITY |
이 일정이 바쁜 시간으로 계산되는지 또는 일정을 예약할 수 있는 한가한 시간인지 여부입니다. |
GUESTS_CAN_MODIFY |
참석자가 일정을 수정할 수 있는지 여부입니다. |
GUESTS_CAN_INVITE_OTHERS |
참석자가 다른 참석자를 초대할 수 있는지 여부 |
GUESTS_CAN_SEE_GUESTS |
참석자가 참석자 목록을 볼 수 있는지 여부입니다. |
일정 추가
애플리케이션에서 새 일정을 삽입할 때는 인텐트를 사용하여 일정 삽입에 설명된 대로 INSERT 인텐트를 사용하는 것이 좋습니다. 하지만 필요한 경우 이벤트를 직접 삽입할 수 있습니다. 이 섹션에서는 이 작업을 수행하는 방법을 설명합니다.
새 이벤트를 삽입하는 규칙은 다음과 같습니다.
CALENDAR_ID및DTSTART를 포함해야 합니다.EVENT_TIMEZONE을 포함해야 합니다. 시스템에 설치된 시간대 ID 목록을 가져오려면getAvailableIDs()를 사용합니다. 인텐트를 사용하여 일정 삽입에 설명된INSERT인텐트를 통해 일정을 삽입하는 경우에는 이 규칙이 적용되지 않습니다. 이 시나리오에서는 기본 시간대가 제공됩니다.- 반복되지 않는 일정의 경우
DTEND를 포함해야 합니다. - 반복 일정의 경우
RRULE또는RDATE외에DURATION를 포함해야 합니다. 인텐트를 사용하여 일정 삽입에 설명된INSERT인텐트를 통해 일정을 삽입하는 경우에는 이 규칙이 적용되지 않습니다. 이 시나리오에서는RRULE를DTSTART및DTEND와 함께 사용할 수 있으며 Calendar 애플리케이션이 이를 자동으로 기간으로 변환합니다.
다음은 일정을 삽입하는 예시입니다. 간단한 예를 위해 UI 스레드에서 실행됩니다. 실제로는 작업을 백그라운드 스레드로 이동하기 위해 비동기 스레드에서 삽입 및 업데이트를 실행해야 합니다. 자세한 내용은 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 // //
자바
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 // //
참고: 이 예시에서 이벤트가 생성된 후 이벤트 ID를 캡처하는 방법을 확인하세요. 이 방법이 이벤트 ID를 가져오는 가장 쉬운 방법입니다. 일정에 참석자 또는 리마인더를 추가하는 등 다른 캘린더 작업을 수행하려면 일정 ID가 필요한 경우가 많습니다.
업데이트 이벤트
애플리케이션에서 사용자가 일정을 수정하도록 허용하려면 인텐트를 사용하여 일정 수정에 설명된 대로 EDIT 인텐트를 사용하는 것이 좋습니다.
하지만 필요한 경우 일정을 직접 수정할 수 있습니다. 이벤트를 업데이트하려면 이벤트의 _ID를 URI에 추가된 ID (withAppendedId()) 또는 첫 번째 선택 항목으로 제공하면 됩니다.
선택은 "_id=?"로 시작해야 하며 첫 번째 selectionArg는 이벤트의 _ID여야 합니다. ID가 없는 선택을 사용하여 업데이트를 수행할 수도 있습니다. 다음은 이벤트를 업데이트하는 예시입니다. 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")
자바
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);
일정 삭제
URI에 추가된 ID로 _ID를 사용하거나 표준 선택을 사용하여 이벤트를 삭제할 수 있습니다. 추가된 ID를 사용하는 경우 선택할 수도 없습니다.
삭제에는 애플리케이션과 동기화 어댑터라는 두 가지 버전이 있습니다. 애플리케이션 삭제는 deleted 열을 1로 설정합니다. 이 플래그는 행이 삭제되었으며 이 삭제가 서버에 전파되어야 함을 동기화 어댑터에 알립니다. 동기화 어댑터 삭제는 데이터베이스에서 일정과 관련된 모든 데이터를 삭제합니다. 다음은 _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")
자바
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);
참석자 표
CalendarContract.Attendees 테이블의 각 행은 일정의 단일 참석자 또는 게스트를 나타냅니다. query()를 호출하면 지정된 EVENT_ID가 있는 일정의 참석자 목록이 반환됩니다.
이 EVENT_ID는 특정 이벤트의 _ID와 일치해야 합니다.
다음 표에는 쓰기 가능한 필드가 나와 있습니다. 새 참석자를 삽입할 때는 ATTENDEE_NAME를 제외한 모든 참석자를 포함해야 합니다.
| 상수 | 설명 |
|---|---|
EVENT_ID |
이벤트 ID입니다. |
ATTENDEE_NAME |
참석자의 이름입니다. |
ATTENDEE_EMAIL |
참석자의 이메일 주소입니다. |
ATTENDEE_RELATIONSHIP |
참석자와 일정의 관계입니다. 다음 중 하나를 선택합니다. |
ATTENDEE_TYPE |
참석자 유형입니다. 다음 중 하나를 선택합니다. |
ATTENDEE_STATUS |
참석자의 참석 상태입니다. 다음 중 하나를 선택합니다. |
참석자 추가
다음은 일정에 참석자 한 명을 추가하는 예입니다. 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)
자바
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);
알림 표
CalendarContract.Reminders 테이블의 각 행은 일정의 단일 알림을 나타냅니다. query()를 호출하면 지정된 EVENT_ID가 있는 일정의 알림 목록이 반환됩니다.
다음 표에는 리마인더의 쓰기 가능한 필드가 나와 있습니다. 새 리마인더를 삽입할 때 모두 포함해야 합니다. 동기화 어댑터는 CalendarContract.Calendars 테이블에서 지원하는 알림 유형을 지정합니다. 자세한 내용은 ALLOWED_REMINDERS를 참고하세요.
| 상수 | 설명 |
|---|---|
EVENT_ID |
이벤트 ID입니다. |
MINUTES |
이벤트가 시작되기 몇 분 전에 알림을 실행할지 지정합니다. |
METHOD |
서버에 설정된 알람 방법입니다. 다음 중 하나를 선택합니다. |
알림 추가
이 예시에서는 일정에 알림을 추가합니다. 알림은 일정 15분 전에 실행됩니다.
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)
자바
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);
인스턴스 테이블
CalendarContract.Instances 테이블에는 이벤트 발생의 시작 및 종료 시간이 포함됩니다. 이 테이블의 각 행은
단일 이벤트 발생을 나타냅니다 인스턴스 테이블은 쓰기 가능하지 않으며 이벤트 발생을 쿼리하는 방법만 제공합니다.
다음 표에는 인스턴스에 대해 쿼리할 수 있는 일부 필드가 나와 있습니다. 시간대는 KEY_TIMEZONE_TYPE 및 KEY_TIMEZONE_INSTANCES로 정의됩니다.
| 상수 | 설명 |
|---|---|
BEGIN |
인스턴스의 시작 시간(UTC 밀리초)입니다. |
END |
인스턴스의 종료 시간(UTC 밀리초)입니다. |
END_DAY |
캘린더의 시간대를 기준으로 한 인스턴스의 줄리안 종료일입니다. |
END_MINUTE |
캘린더 시간대의 자정부터 측정된 인스턴스의 종료 분입니다. |
EVENT_ID |
이 인스턴스에 대한 이벤트의 _ID입니다. |
START_DAY |
캘린더의 시간대를 기준으로 한 인스턴스의 율리우스력 시작일입니다. |
START_MINUTE |
자정부터 측정된 인스턴스의 시작 시간(분)으로, Calendar의 시간대를 기준으로 합니다. |
인스턴스 테이블 쿼리
Instances 테이블을 쿼리하려면 URI에서 쿼리의 기간을 지정해야 합니다. 이 예시에서 CalendarContract.Instances는 CalendarContract.EventsColumns 인터페이스 구현을 통해 TITLE 필드에 액세스합니다.
즉, TITLE는 원시 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)}") }
자바
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())); } }
Calendar 인텐트
애플리케이션에 캘린더 데이터를 읽고 쓰는 권한이 필요하지 않습니다. 대신 Android 캘린더 애플리케이션에서 지원하는 인텐트를 사용하여 읽기 및 쓰기 작업을 해당 애플리케이션에 전달할 수 있습니다. 다음 표에는 Calendar Provider에서 지원하는 인텐트가 나와 있습니다.
| 작업 | URI | 설명 | 추가 |
|---|---|---|---|
VIEW |
CalendarContract.CONTENT_URI를 사용하여 URI를 참조할 수도 있습니다.
이 인텐트를 사용하는 예는 인텐트를 사용하여 캘린더 데이터 보기를 참고하세요.
|
<ms_since_epoch>에 지정된 시간으로 캘린더를 엽니다. |
없음 |
Events.CONTENT_URI를 사용하여 URI를 참조할 수도 있습니다.
이 인텐트를 사용하는 예는 인텐트를 사용하여 캘린더 데이터 보기를 참고하세요.
|
<event_id>에서 지정한 이벤트를 확인합니다. |
CalendarContract.EXTRA_EVENT_BEGIN_TIMECalendarContract.EXTRA_EVENT_END_TIME |
|
EDIT |
Events.CONTENT_URI를 사용하여 URI를 참조할 수도 있습니다.
이 인텐트를 사용하는 예는 인텐트를 사용하여 일정 수정을 참고하세요.
|
<event_id>로 지정된 일정을 수정합니다. |
CalendarContract.EXTRA_EVENT_BEGIN_TIMECalendarContract.EXTRA_EVENT_END_TIME |
EDIT INSERT |
Events.CONTENT_URI를 사용하여 URI를 참조할 수도 있습니다.
이 인텐트를 사용하는 예는 인텐트를 사용하여 일정 삽입을 참고하세요.
|
일정을 만듭니다. | 다음 표에 나열된 추가 항목 |
다음 표에는 Calendar Provider에서 지원하는 인텐트 추가 항목이 나와 있습니다.
| 인텐트 추가 항목 | 설명 |
|---|---|
Events.TITLE |
이벤트 이름입니다. |
CalendarContract.EXTRA_EVENT_BEGIN_TIME |
에포크 이후의 이벤트 시작 시간(밀리초)입니다. |
CalendarContract.EXTRA_EVENT_END_TIME |
에포크 이후의 이벤트 종료 시간(밀리초)입니다. |
CalendarContract.EXTRA_EVENT_ALL_DAY |
일정이 종일 일정임을 나타내는 불리언입니다. 값은 true 또는 false일 수 있습니다. |
Events.EVENT_LOCATION |
이벤트의 위치 |
Events.DESCRIPTION |
이벤트 설명입니다. |
Intent.EXTRA_EMAIL |
초대할 사용자의 이메일 주소(쉼표로 구분된 목록)입니다. |
Events.RRULE |
이벤트의 반복 규칙입니다. |
Events.ACCESS_LEVEL |
일정이 비공개인지 공개인지 여부 |
Events.AVAILABILITY |
이 일정이 바쁜 시간으로 계산되는지 또는 일정을 예약할 수 있는 한가한 시간인지 여부입니다. |
다음 섹션에서는 이러한 인텐트를 사용하는 방법을 설명합니다.
인텐트를 사용하여 이벤트 삽입
INSERT 인텐트를 사용하면 애플리케이션이 일정 삽입 작업을 Calendar 자체에 전달할 수 있습니다.
이 접근 방식을 사용하면 애플리케이션의 매니페스트 파일에 WRITE_CALENDAR 권한을 포함할 필요가 없습니다.
사용자가 이 접근 방식을 사용하는 애플리케이션을 실행하면 애플리케이션은 사용자를 Calendar로 보내 일정을 추가하도록 합니다. INSERT 인텐트는 추가 필드를 사용하여 Calendar의 일정 세부정보로 양식을 자동 입력합니다. 그러면 사용자는 일정을 취소하거나, 필요에 따라 양식을 수정하거나, 일정을 캘린더에 저장할 수 있습니다.
다음은 2012년 1월 19일 오전 7시 30분부터 오전 8시 30분까지 실행되는 이벤트를 예약하는 코드 스니펫입니다. 이 코드 스니펫에 관해 다음 사항에 유의하세요.
Events.CONTENT_URI를 URI로 지정합니다.CalendarContract.EXTRA_EVENT_BEGIN_TIME및CalendarContract.EXTRA_EVENT_END_TIME추가 필드를 사용하여 이벤트 시간으로 양식을 자동 입력합니다. 이러한 시간의 값은 에포크 이후의 UTC 밀리초 단위여야 합니다.Intent.EXTRA_EMAIL추가 필드를 사용하여 이메일 주소로 지정된 초대 대상자의 쉼표로 구분된 목록을 제공합니다.
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)
자바
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);
인텐트를 사용하여 이벤트 수정
이벤트 업데이트에 설명된 대로 이벤트를 직접 업데이트할 수 있습니다. 하지만 EDIT 인텐트를 사용하면 권한이 없는 애플리케이션이 일정 수정을 Calendar 애플리케이션에 전달할 수 있습니다.
사용자가 Calendar에서 일정 수정을 완료하면 원래 애플리케이션으로 돌아갑니다.
다음은 지정된 일정에 새 제목을 설정하고 사용자가 Calendar에서 일정을 수정할 수 있도록 하는 인텐트의 예입니다.
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)
자바
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);
인텐트를 사용하여 캘린더 데이터 보기
Calendar Provider는 VIEW 인텐트를 사용하는 두 가지 방법을 제공합니다.
- 특정 날짜의 Calendar를 엽니다.
- 일정을 확인합니다.
다음은 특정 날짜에 캘린더를 여는 방법을 보여주는 예입니다.
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)
자바
// 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);
다음은 이벤트를 열어 보는 방법을 보여주는 예입니다.
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)
자바
long eventID = 208; ... Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); Intent intent = new Intent(Intent.ACTION_VIEW) .setData(uri); startActivity(intent);
동기화 어댑터
애플리케이션과 동기화 어댑터가 Calendar 제공자에 액세스하는 방식에는 사소한 차이만 있습니다.
- 동기화 어댑터는
CALLER_IS_SYNCADAPTER를true로 설정하여 동기화 어댑터임을 지정해야 합니다. - 동기화 어댑터는 URI에 쿼리 매개변수로
ACCOUNT_NAME및ACCOUNT_TYPE를 제공해야 합니다. - 동기화 어댑터는 애플리케이션이나 위젯보다 더 많은 열에 대한 쓰기 액세스 권한을 가집니다.
예를 들어 애플리케이션은 이름, 표시 이름, 공개 상태 설정, 캘린더 동기화 여부와 같은 캘린더의 몇 가지 특성만 수정할 수 있습니다. 이와 비교하여 동기화 어댑터는 이러한 열뿐만 아니라 캘린더 색상, 시간대, 액세스 수준, 위치와 같은 다른 많은 열에도 액세스할 수 있습니다.
그러나 동기화 어댑터는 지정된
ACCOUNT_NAME및ACCOUNT_TYPE로 제한됩니다.
다음은 동기화 어댑터에 사용할 URI를 반환하는 데 사용할 수 있는 도우미 메서드입니다.
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() }
자바
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(); }