Ringkasan penyedia kalender

Penyedia Kalender adalah repositori untuk acara kalender pengguna. Calendar Provider API memungkinkan Anda melakukan operasi kueri, menyisipkan, memperbarui, dan menghapus pada kalender, acara, tamu, pengingat, dan sebagainya.

Calender Provider API bisa digunakan oleh aplikasi dan adaptor sinkronisasi. Aturannya bervariasi bergantung pada jenis program yang melakukan panggilan. Dokumen ini terutama berfokus pada penggunaan Calendar Provider API sebagai aplikasi. Untuk pembahasan tentang perbedaan adaptor sinkronisasi, lihat Adaptor Sinkronisasi.

Biasanya, untuk membaca atau menulis data kalender, manifes aplikasi harus berisi izin yang sesuai, yang dijelaskan dalam Izin Pengguna. Untuk mempermudah dilakukannya operasi umum, Penyedia Kalender menawarkan serangkaian intent, seperti yang dijelaskan dalam Intent Kalender. Semua intent ini membawa pengguna ke aplikasi Kalender untuk menyisipkan, melihat, dan mengedit acara. Pengguna berinteraksi dengan aplikasi Kalender, lalu kembali ke aplikasi semula. Jadi, aplikasi Anda tidak perlu meminta izin, juga tidak perlu menyediakan antarmuka pengguna untuk melihat atau membuat peristiwa.

Dasar-dasar

Penyedia konten menyimpan data dan membuatnya dapat diakses oleh aplikasi. Penyedia konten yang ditawarkan oleh platform Android (termasuk Penyedia Kalender) biasanya mengekspos data sebagai satu set tabel berdasarkan model database relasional, dengan tiap baris berupa catatan dan tiap kolom berupa data yang memiliki tipe dan arti tertentu. Melalui Calendar Provider API, aplikasi dan adaptor sinkronisasi bisa mendapatkan akses baca/tulis ke tabel database yang menyimpan data kalender pengguna.

Setiap penyedia konten mengekspos URI publik (yang dibungkus sebagai objek Uri ) yang mengidentifikasi set datanya secara unik. Penyedia konten yang mengontrol beberapa set data (beberapa tabel) mengekspos URI terpisah untuk tiap set. Semua URI untuk penyedia diawali dengan string "content://". Hal ini mengidentifikasi data sebagai dikontrol oleh penyedia konten. Penyedia Kalender menentukan konstanta untuk URI untuk setiap class-nya (tabel). URI ini memiliki format <class>.CONTENT_URI. Misalnya, Events.CONTENT_URI.

Gambar 1 menampilkan representasi grafis model data Penyedia Kalender. Gambar ini menampilkan tabel utama dan kolom yang saling menautkan.

Model Data Penyedia Kalender

Gambar 1. Model data Penyedia Kalender.

Seorang pengguna bisa memiliki beberapa kalender, dan kalender yang berbeda bisa dikaitkan dengan tipe akun yang berbeda (Google Kalender, Exchange, dan seterusnya).

CalendarContract menentukan model data informasi yang terkait dengan kalender dan acara. Data ini disimpan di sejumlah tabel, yang dicantumkan di bawah ini.

Tabel (Kelas) Deskripsi

CalendarContract.Calendars

Tabel ini menyimpan informasi khusus kalender. Setiap baris dalam tabel ini berisi detail untuk satu kalender, seperti nama, warna, informasi sinkronisasi, dan sebagainya.
CalendarContract.Events Tabel ini menyimpan informasi khusus peristiwa. Setiap baris dalam tabel ini berisi informasi untuk satu peristiwa, misalnya judul peristiwa, lokasi, waktu mulai, waktu berakhir, dan sebagainya. Kejadian bisa terjadi satu kali atau bisa berulang beberapa kali. Peserta, pengingat, dan properti perluasan disimpan dalam tabel terpisah. Masing-masing memiliki EVENT_ID yang mereferensikan _ID dalam tabel Peristiwa.
CalendarContract.Instances Tabel ini menyimpan waktu mulai dan berakhir untuk setiap kemunculan peristiwa. Setiap baris dalam tabel ini mewakili satu bentuk kekerapan. Untuk peristiwa satu kali, ada pemetaan 1:1 antara instance dan peristiwa. Untuk peristiwa berulang, beberapa baris akan dibuat secara otomatis yang sesuai dengan beberapa kejadian tersebut.
CalendarContract.Attendees Tabel ini menyimpan informasi peserta (tamu) peristiwa. Setiap baris mewakili satu tamu dari peristiwa. Ini menentukan jenis tamu dan respons kehadiran tamu untuk acara.
CalendarContract.Reminders Tabel ini menyimpan data pemberitahuan/notifikasi. Tiap baris mewakili satu peringatan untuk sebuah kejadian. Peristiwa dapat memiliki beberapa pengingat. Jumlah maksimum pengingat per peristiwa ditetapkan dalam MAX_REMINDERS, yang ditetapkan oleh adaptor sinkronisasi yang memiliki kalender yang diberikan. Pengingat ditetapkan dalam menit sebelum peristiwa dan memiliki metode yang menentukan cara pengguna akan diperingatkan.

Calendar Provider API didesain agar luwes dan tangguh. Sementara itu, Anda perlu memberikan pengalaman pengguna akhir yang baik dan melindungi integritas kalender dan datanya. Untuk mencapainya, berikut beberapa hal yang perlu diingat saat menggunakan API ini:

  • Menyisipkan, memperbarui, dan melihat acara kalender. Untuk menyisipkan, mengubah, dan membaca peristiwa secara langsung dari Penyedia Kalender, Anda memerlukan izin yang sesuai. Namun, jika Anda tidak sedang membangun aplikasi atau adaptor sinkronisasi kalender berfitur lengkap, maka tidak perlu meminta izin. Sebagai gantinya, Anda bisa menggunakan intent yang didukung oleh aplikasi Kalender Android untuk menyerahkan operasi baca dan tulis ke aplikasi itu. Saat Anda menggunakan intent, aplikasi Anda akan mengirim pengguna ke aplikasi Kalender untuk melakukan operasi yang diinginkan dalam formulir yang sudah diisi. Setelah selesai, formulir akan dikembalikan ke aplikasi Anda. Dengan mendesain aplikasi untuk melakukan operasi umum melalui Kalender, Anda akan memberi pengguna sebuah antarmuka pengguna yang konsisten dan tangguh. Inilah pendekatan yang direkomendasikan. Untuk informasi selengkapnya, lihat Intent Kalender.
  • Adaptor sinkronisasi. Adaptor sinkronisasi menyinkronkan data kalender pada perangkat pengguna dengan server atau sumber data lain. Dalam tabel CalendarContract.Calendars dan CalendarContract.Events, ada kolom yang dicadangkan untuk digunakan adaptor sinkronisasi. Penyedia dan aplikasi tidak boleh memodifikasinya. Bahkan, tabel tersebut tidak terlihat kecuali jika diakses sebagai adaptor sinkronisasi. Untuk informasi selengkapnya tentang adaptor sinkronisasi, lihat Adaptor Sinkronisasi.

Izin pengguna

Untuk membaca data kalender, aplikasi harus menyertakan izin READ_CALENDAR dalam file manifesnya. File ini harus menyertakan izin WRITE_CALENDAR untuk menghapus, menyisipkan, atau memperbarui data kalender:

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

Tabel kalender

Tabel CalendarContract.Calendars berisi detail untuk setiap kalender. Kolom Kalender berikut dapat ditulis oleh aplikasi dan adaptor sinkronisasi. Untuk mengetahui daftar lengkap kolom yang didukung, lihat referensi CalendarContract.Calendars.

Konstanta Deskripsi
NAME Nama kalender.
CALENDAR_DISPLAY_NAME Nama kalender ini yang ditampilkan kepada pengguna.
VISIBLE Sebuah boolean yang menunjukkan apakah kalender dipilih untuk ditampilkan. Nilai 0 menunjukkan bahwa acara yang terkait dengan kalender ini tidak boleh ditampilkan. Nilai 1 menunjukkan bahwa peristiwa yang terkait dengan kalender ini harus ditampilkan. Nilai ini memengaruhi pembuatan baris dalam tabel CalendarContract.Instances.
SYNC_EVENTS Boolean yang menunjukkan apakah kalender harus disinkronkan dan apakah peristiwanya harus disimpan di perangkat. Nilai 0 berarti jangan menyinkronkan kalender ini atau simpan peristiwanya di perangkat. Nilai 1 berarti menyinkronkan peristiwa untuk kalender ini dan menyimpan peristiwanya di perangkat.

Menyertakan tipe akun untuk semua operasi

Jika Anda membuat kueri pada Calendars.ACCOUNT_NAME, Anda juga harus menyertakan Calendars.ACCOUNT_TYPE dalam pilihan. Hal ini karena akun tertentu hanya dianggap unik mengingat ACCOUNT_NAME dan ACCOUNT_TYPE-nya. ACCOUNT_TYPE adalah string yang sesuai dengan autentikator akun yang digunakan saat akun didaftarkan dengan AccountManager. Ada juga jenis akun khusus yang disebut ACCOUNT_TYPE_LOCAL untuk kalender yang tidak terkait dengan akun perangkat. Akun ACCOUNT_TYPE_LOCAL tidak disinkronkan.

Membuat kueri kalender

Berikut adalah contoh yang menunjukkan cara mendapatkan kalender yang dimiliki oleh pengguna tertentu. Untuk memudahkan, dalam contoh ini, operasi kueri ditampilkan dalam thread antarmuka pengguna ("thread utama"). Dalam praktiknya, hal ini harus dilakukan dalam thread asinkron, bukan di thread utama. Untuk pembahasan lebih lanjut, lihat Loader. Jika Anda tidak hanya membaca data, tetapi memodifikasinya, lihat 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;

Di bagian berikutnya pada contoh ini, Anda akan melakukan kueri. Pemilihan akan menentukan kriteria untuk kueri. Dalam contoh ini, kueri mencari kalender yang memiliki ACCOUNT_NAME "hera@example.com", ACCOUNT_TYPE "com.example", dan OWNER_ACCOUNT "hera@example.com". Jika Anda ingin melihat semua kalender yang telah dilihat pengguna, bukan hanya kalender yang dimiliki pengguna, hapus OWNER_ACCOUNT. Kueri menampilkan objek Cursor yang dapat Anda gunakan untuk menjelajahi kumpulan hasil yang ditampilkan oleh kueri database. Untuk diskusi selengkapnya tentang penggunaan kueri dalam penyedia konten, lihat Penyedia Konten.

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

Bagian berikutnya ini menggunakan kursor untuk merunut set hasil. Bagian ini menggunakan konstanta yang disiapkan di awal contoh untuk menampilkan nilai untuk setiap kolom.

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

   ...
}

Memodifikasi kalender

Untuk melakukan pembaruan kalender, Anda dapat memberikan _ID kalender sebagai ID yang ditambahkan ke URI (withAppendedId()) atau sebagai item pilihan pertama. Pemilihan harus dimulai dengan "_id=?", dan selectionArg pertama harus berupa _ID kalender. Anda juga bisa melakukan pembaruan dengan mengkodekan ID dalam URI. Contoh ini mengubah nama tampilan kalender menggunakan pendekatan (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);

Menyisipkan kalender

Kalender didesain untuk dikelola terutama oleh sebuah adaptor sinkronisasi, sehingga Anda hanya boleh menyisipkan kalender baru sebagai adaptor sinkronisasi. Biasanya, aplikasi hanya dapat membuat perubahan semu pada kalender, seperti mengubah nama tampilan. Jika aplikasi perlu membuat kalender lokal, aplikasi dapat melakukannya dengan melakukan penyisipan kalender sebagai adaptor sinkronisasi, menggunakan ACCOUNT_TYPE dari ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCAL adalah jenis akun khusus untuk kalender yang tidak terkait dengan akun perangkat. Kalender tipe ini tidak disinkronkan dengan server. Untuk diskusi tentang adaptor sinkronisasi, lihat Adaptor Sinkronisasi.

Tabel Events

Tabel CalendarContract.Events berisi detail untuk setiap peristiwa. Untuk menambahkan, memperbarui, atau menghapus peristiwa, aplikasi harus menyertakan izin WRITE_CALENDAR dalam file manifes-nya.

Kolom Events berikut ini dapat ditulis oleh aplikasi dan adaptor sinkronisasi. Untuk mengetahui daftar lengkap kolom yang didukung, lihat referensi CalendarContract.Events.

Konstanta Deskripsi
CALENDAR_ID _ID dari kalender yang memiliki peristiwa.
ORGANIZER Email penyelenggara (pemilik) kejadian.
TITLE Judul kejadian.
EVENT_LOCATION Tempat kejadian.
DESCRIPTION Keterangan kejadian.
DTSTART Waktu mulai kejadian dalam milidetik UTC sejak waktu patokan.
DTEND Waktu selesai kejadian dalam milidetik UTC sejak waktu patokan.
EVENT_TIMEZONE Zona waktu kejadian.
EVENT_END_TIMEZONE Zona waktu untuk waktu selesai kejadian.
DURATION Durasi peristiwa dalam format RFC5545. Misalnya, nilai "PT1H" menyatakan bahwa peristiwa harus berlangsung satu jam, dan nilai "P2W" menunjukkan durasi 2 minggu.
ALL_DAY Nilai 1 menunjukkan bahwa peristiwa ini berlangsung sepanjang hari, seperti yang ditetapkan oleh zona waktu lokal. Nilai 0 menunjukkan bahwa peristiwa tersebut adalah peristiwa biasa yang dapat dimulai dan berakhir kapan saja selama sehari.
RRULE Aturan perulangan untuk format kejadian. Misalnya, "FREQ=WEEKLY;COUNT=10;WKST=SU". Anda dapat menemukan contoh lainnya di sini.
RDATE Tanggal perulangan kejadian. Anda biasanya menggunakan RDATE bersama dengan RRULE untuk menentukan kumpulan agregat kejadian berulang. Untuk diskusi selengkapnya, lihat spesifikasi RFC5545.
AVAILABILITY Jika peristiwa ini dihitung sebagai waktu sibuk atau waktu luang yang dapat dijadwalkan.
GUESTS_CAN_MODIFY Apakah tamu bisa memodifikasi kejadian atau tidak.
GUESTS_CAN_INVITE_OTHERS Apakah tamu bisa mengundang tamu lain atau tidak.
GUESTS_CAN_SEE_GUESTS Apakah tamu bisa membaca daftar peserta atau tidak.

Tambahkan kejadian

Saat aplikasi menyisipkan peristiwa baru, sebaiknya gunakan Intent INSERT, seperti yang dijelaskan dalam Menggunakan intent untuk menyisipkan peristiwa. Namun, jika perlu, Anda dapat menyisipkan peristiwa secara langsung. Bagian ini menjelaskan cara melakukannya.

Berikut ini adalah aturan untuk menyisipkan kejadian baru:

Berikut ini adalah contoh penyisipan kejadian. Penyisipan ini dilakukan dalam thread UI demi kemudahan. Dalam praktiknya, penyisipan dan pembaruan harus dilakukan di thread asinkron untuk memindahkan tindakan ke dalam thread latar belakang. Untuk mengetahui informasi selengkapnya, lihat 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
//
//

Catatan: Lihat cara contoh ini menangkap ID peristiwa setelah peristiwa dibuat. Inilah cara termudah untuk mendapatkan ID kejadian. Anda sering kali memerlukan ID acara untuk melakukan operasi kalender lainnya—misalnya, untuk menambahkan peserta atau pengingat ke acara.

Peristiwa update

Jika aplikasi Anda ingin mengizinkan pengguna mengedit peristiwa, sebaiknya gunakan Intent EDIT, seperti dijelaskan dalam Menggunakan intent untuk mengedit peristiwa. Akan tetapi, jika perlu, Anda bisa menyisipkan kejadian secara langsung. Untuk melakukan pembaruan Peristiwa, Anda dapat memberikan _ID peristiwa sebagai ID yang ditambahkan ke URI (withAppendedId()) atau sebagai item pilihan pertama. Pilihan harus dimulai dengan "_id=?", dan selectionArg pertama harus berupa _ID peristiwa. Anda juga dapat melakukan pembaruan dengan menggunakan pemilihan tanpa ID. Berikut adalah contoh pembaruan peristiwa. Contoh ini mengubah judul peristiwa menggunakan pendekatan 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);

Hapus acara

Anda dapat menghapus peristiwa menggunakan _ID sebagai ID yang ditambahkan pada URI, atau menggunakan pemilihan standar. Jika menggunakan ID yang ditambahkan, Anda juga tidak dapat melakukan pemilihan. Ada dua versi penghapusan: sebagai aplikasi dan adaptor sinkronisasi. Penghapusan aplikasi menetapkan kolom dihapus ke 1. Flag ini yang memberi tahu adaptor sinkronisasi bahwa baris telah dihapus dan penghapusan ini harus disebarkan ke server. Penghapusan adaptor sinkronisasi akan menghapus peristiwa dari database bersama semua data terkaitnya. Berikut adalah contoh aplikasi yang menghapus peristiwa melalui _ID-nya:

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

Tabel peserta

Setiap baris tabel CalendarContract.Attendees mewakili satu tamu atau peserta acara. Memanggil query() akan menampilkan daftar peserta untuk peristiwa dengan EVENT_ID yang diberikan. EVENT_ID ini harus cocok dengan _ID peristiwa tertentu.

Tabel berikut mencantumkan kolom-kolom yang dapat ditulis. Saat menyisipkan peserta baru, Anda harus menyertakan semuanya kecuali ATTENDEE_NAME.

Konstanta Deskripsi
EVENT_ID ID kejadian.
ATTENDEE_NAME Nama peserta.
ATTENDEE_EMAIL Alamat email peserta.
ATTENDEE_RELATIONSHIP

Hubungan peserta dengan kejadian. Salah satu dari:

ATTENDEE_TYPE

Tipe peserta. Salah satu dari:

ATTENDEE_STATUS

Status kehadiran peserta. Salah satu dari:

Menambahkan peserta

Berikut ini adalah contoh yang menambahkan satu peserta ke kejadian. Perhatikan bahwa EVENT_ID diperlukan:

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

Tabel pengingat

Setiap baris tabel CalendarContract.Reminders mewakili satu pengingat untuk sebuah peristiwa. Memanggil query() akan menampilkan daftar pengingat untuk peristiwa dengan EVENT_ID yang diberikan.

Tabel berikut mencantumkan bidang-bidang yang bisa ditulis untuk pengingat. Semuanya harus disertakan saat menyisipkan pengingat baru. Perlu diketahui bahwa adaptor sinkronisasi menentukan jenis pengingat yang didukungnya dalam tabel CalendarContract.Calendars. Lihat ALLOWED_REMINDERS untuk mengetahui detailnya.

Konstanta Deskripsi
EVENT_ID ID kejadian.
MINUTES Menit yang ditunggu untuk memicu kejadian pengingat.
METHOD

Metode alarm, seperti yang diatur pada server. Salah satu dari:

Tambahkan pengingat

Contoh ini menambahkan pengingat ke kejadian. Pengingat dipicu 15 menit sebelum peristiwa.

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

Tabel instance

Tabel CalendarContract.Instances menyimpan waktu mulai dan berakhir untuk kemunculan peristiwa. Setiap baris dalam tabel ini mewakili satu bentuk kekerapan. Tabel instance tidak dapat ditulis dan hanya menyediakan cara untuk membuat kueri kekerapan.

Tabel berikut mencantumkan beberapa kolom yang bisa Anda kueri untuk suatu instance. Perhatikan bahwa zona waktu ditentukan oleh KEY_TIMEZONE_TYPE dan KEY_TIMEZONE_INSTANCES.

Konstanta Deskripsi
BEGIN Waktu mulai instance, dalam milidetik UTC.
END Waktu selesai instance, dalam milidetik UTC.
END_DAY Hari akhir Julian dari instance, relatif terhadap zona waktu Kalender.
END_MINUTE Menit berakhir dari instance yang diukur dari tengah malam di zona waktu Kalender.
EVENT_ID _ID peristiwa untuk instance ini.
START_DAY Hari mulai Julian dari instance, relatif terhadap zona waktu Kalender.
START_MINUTE Menit mulai dari instance yang diukur dari tengah malam, relatif terhadap zona waktu Kalender.

Membuat kueri tabel Instance

Untuk membuat kueri tabel Instance, Anda perlu menentukan rentang waktu untuk kueri dalam URI. Dalam contoh ini, CalendarContract.Instances mendapatkan akses ke kolom TITLE melalui implementasi antarmuka CalendarContract.EventsColumns. Dengan kata lain, TITLE ditampilkan melalui tampilan database, bukan melalui kueri tabel CalendarContract.Instances mentah.

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

Intent kalender

Aplikasi Anda tidak memerlukan izin untuk membaca dan menulis data kalender. Sebagai gantinya, aplikasi bisa menggunakan intent yang didukung oleh aplikasi Kalender Android untuk menyerahkan operasi baca dan tulis ke aplikasi itu. Tabel berikut mencantumkan maksud yang didukung oleh Penyedia Kalender:

Tindakan URI Deskripsi Konten bonus

VIEW

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

Anda juga dapat merujuk ke URI dengan CalendarContract.CONTENT_URI. Untuk contoh penggunaan intent ini, lihat Menggunakan intent untuk melihat data kalender.
Buka kalender ke waktu yang ditentukan oleh <ms_since_epoch>. Tidak ada.

VIEW

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

Anda juga dapat merujuk ke URI dengan Events.CONTENT_URI. Untuk contoh penggunaan intent ini, lihat Menggunakan intent untuk melihat data kalender.
Menampilkan peristiwa yang ditentukan oleh <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

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

Anda juga dapat merujuk ke URI dengan Events.CONTENT_URI. Untuk contoh penggunaan intent ini, lihat Menggunakan intent untuk mengedit peristiwa.
Mengedit peristiwa yang ditentukan oleh <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

Anda juga dapat merujuk ke URI dengan Events.CONTENT_URI. Untuk contoh penggunaan intent ini, lihat Menggunakan intent untuk menyisipkan peristiwa.
Buat acara. Ekstra apa saja yang tercantum dalam tabel di bawah.

Tabel berikut mencantumkan tambahan maksud yang didukung oleh Penyedia Kalender:

Ekstra Maksud Deskripsi
Events.TITLE Nama kejadian.
CalendarContract.EXTRA_EVENT_BEGIN_TIME Waktu mulai kejadian dalam milidetik sejak waktu patokan.
CalendarContract.EXTRA_EVENT_END_TIME Waktu selesai kejadian dalam milidetik sejak waktu patokan.
CalendarContract.EXTRA_EVENT_ALL_DAY Sebuah boolean yang menunjukkan bahwa kejadian sehari penuh. Nilainya dapat berupa true atau false.
Events.EVENT_LOCATION Lokasi kejadian.
Events.DESCRIPTION Keterangan kejadian.
Intent.EXTRA_EMAIL Alamat email mereka yang harus diundang berupa daftar yang dipisahkan koma.
Events.RRULE Aturan perulangan kejadian.
Events.ACCESS_LEVEL Apakah kejadian bersifat privat atau publik.
Events.AVAILABILITY Jika kejadian ini dihitung sebagai waktu sibuk atau waktu bebas yang bisa dijadwalkan.

Bagian berikut menjelaskan cara menggunakan semua intent ini.

Menggunakan intent untuk menyisipkan kejadian

Penggunaan Intent INSERT memungkinkan aplikasi Anda menyerahkan tugas penyisipan acara ke Kalender itu sendiri. Dengan pendekatan ini, aplikasi Anda bahkan tidak perlu menyertakan izin WRITE_CALENDAR dalam file manifes-nya.

Saat pengguna menjalankan aplikasi yang menggunakan pendekatan ini, aplikasi tersebut akan mengirim mereka ke Kalender untuk menyelesaikan penambahan acara. Intent INSERT menggunakan kolom tambahan untuk mengisi formulir lebih dahulu dengan detail acara di Kalender. Pengguna kemudian dapat membatalkan acara, mengedit formulir sesuai kebutuhan, atau menyimpan acara ke kalender mereka.

Berikut ini adalah cuplikan kode yang menjadwalkan peristiwa pada 19 Januari 2012, yang berjalan dari pukul 07.30 hingga 08.30. Perhatikan hal-hal berikut tentang cuplikan kode ini:

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

Menggunakan intent untuk mengedit kejadian

Anda dapat memperbarui peristiwa secara langsung, seperti yang dijelaskan dalam Memperbarui peristiwa. Namun, penggunaan Intent EDIT memungkinkan aplikasi yang tidak memiliki izin untuk menyerahkan pengeditan acara ke aplikasi Kalender. Saat pengguna selesai mengedit acara di Kalender, mereka akan dikembalikan ke aplikasi asli.

Berikut ini adalah contoh intent yang menetapkan judul baru untuk acara yang ditentukan dan memungkinkan pengguna mengedit acara di Kalender.

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

Menggunakan intent untuk menampilkan data kalender

Penyedia Kalender menawarkan dua cara berbeda untuk menggunakan Intent VIEW:

  • Untuk membuka Kalender pada tanggal tertentu.
  • Untuk menampilkan sebuah kejadian.

Berikut ini adalah contoh yang menampilkan cara membuka Kalender pada tanggal tertentu:

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

Berikut ini adalah contoh yang menampilkan cara membuka kejadian untuk menampilkan:

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

Adaptor sinkronisasi

Hanya ada perbedaan kecil dalam cara aplikasi dan adaptor sinkronisasi mengakses Penyedia Kalender:

  • Adaptor sinkronisasi perlu menentukan bahwa dirinya sebuah adaptor sinkronisasi dengan menyetel CALLER_IS_SYNCADAPTER ke true.
  • Adaptor sinkronisasi perlu menyediakan ACCOUNT_NAME dan ACCOUNT_TYPE sebagai parameter kueri dalam URI.
  • Adaptor sinkronisasi memiliki akses tulis ke lebih banyak kolom daripada aplikasi atau widget. Misalnya, aplikasi hanya dapat mengubah beberapa karakteristik kalender, seperti nama, nama tampilan, setelan visibilitas, dan apakah kalender disinkronkan atau tidak. Sebagai perbandingan, adaptor sinkronisasi bisa mengakses bukan hanya kolom-kolom itu, tetapi banyak kolom lainnya, seperti warna kalender, zona waktu, tingkat akses, lokasi, dan sebagainya. Namun, adaptor sinkronisasi dibatasi pada ACCOUNT_NAME dan ACCOUNT_TYPE yang ditentukannya.

Berikut ini adalah metode pembantu yang bisa Anda gunakan untuk mengembalikan URI bagi penggunaan dengan adaptor sinkronisasi:

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