Penyedia Kontak adalah komponen Android yang tangguh dan fleksibel dalam mengelola repositori data pusat tentang pengguna di perangkat. Penyedia Kontak adalah sumber data yang Anda lihat dalam aplikasi kontak perangkat, dan Anda juga bisa mengakses datanya dalam aplikasi Anda sendiri serta mentransfer data antara perangkat dan layanan online. Penyedia mengakomodasi berbagai sumber data dan mencoba mengelola data sebanyak mungkin untuk setiap pengguna, sehingga organisasinya menjadi kompleks. Karena itu, API penyedia menyertakan satu set class kontrak dan antarmuka ekstensif yang membantu pengambilan dan modifikasi data.
Panduan ini menjelaskan hal-hal berikut:
- Struktur penyedia dasar.
- Cara mengambil data dari penyedia.
- Cara memodifikasi data di penyedia.
- Cara menulis adaptor sinkronisasi untuk menyinkronkan data dari server Anda ke Penyedia Kontak.
Panduan ini beranggapan bahwa Anda mengetahui dasar-dasar penyedia materi Android. Untuk mempelajari lebih lanjut tentang penyedia materi Android, bacalah panduan Dasar-Dasar Penyedia Materi.
Organisasi Penyedia Kontak
Penyedia Kontak adalah komponen penyedia konten Android. Komponen ini memelihara tiga tipe data tentang pengguna, masing-masing disesuaikan dengan tabel yang ditawarkan oleh penyedia, seperti yang terlihat dalam gambar 1:

Gambar 1. Struktur tabel Penyedia Kontak.
Ketiga tabel disebut secara umum menurut nama class kontrak. Class mendefinisikan konstanta untuk URI konten, nama kolom, dan nilai kolom yang digunakan oleh tabel:
-
Tabel
ContactsContract.Contacts
- Baris mewakili orang yang berbeda, berdasarkan agregrasi baris kontak mentah.
-
Tabel
ContactsContract.RawContacts
- Baris berisi rangkuman data seseorang, untuk tipe dan akun pengguna tertentu.
-
Tabel
ContactsContract.Data
- Baris berisi data untuk kontak mentah, seperti alamat email atau nomor ponsel.
Tabel lain yang diwakili oleh class kontrak dalam ContactsContract
adalah tabel tambahan yang digunakan Penyedia Kontak untuk mengelola operasinya atau mendukung fungsi tertentu dalam kontak atau aplikasi telepon perangkat.
Kontak mentah
Kontak mentah mewakili data pengguna yang berasal dari satu tipe akun dan nama akun. Karena Penyedia Kontak memungkinkan lebih dari satu layanan online sebagai sumber data untuk satu pengguna, Penyedia Kontak memungkinkan beberapa kontak mentah untuk pengguna yang sama. Beberapa kontak mentah juga memungkinkan pengguna mengombinasikan data seorang pengguna dari beberapa akun dengan tipe akun yang sama.
Sebagian besar data untuk kontak mentah tidak disimpan dalam tabel ContactsContract.RawContacts
. Sebagai gantinya, data tersebut disimpan dalam satu atau beberapa baris dalam tabel ContactsContract.Data
. Setiap baris data memiliki kolom Data.RAW_CONTACT_ID
yang berisi nilai RawContacts._ID
dari baris ContactsContract.RawContacts
induknya.
Kolom-kolom kontak mentah yang penting
Kolom penting dalam tabel ContactsContract.RawContacts
tercantum dalam tabel 1. Silakan baca catatan yang ada di bawah tabel:
Tabel 1. Kolom-kolom kontak mentah yang penting
Nama kolom | Kegunaan | Catatan |
---|---|---|
ACCOUNT_NAME
|
Nama akun untuk tipe akun yang merupakan sumber kontak mentah ini. Misalnya, nama akun dari akun Google adalah salah satu alamat Gmail pemilik perangkat. Lihat entri berikutnya untuk ACCOUNT_TYPE untuk informasi selengkapnya.
|
Format nama ini khusus untuk tipe akun ini. Format ini tidak harus alamat email. |
ACCOUNT_TYPE
|
Tipe akun yang merupakan sumber kontak mentah ini. Misalnya, tipe akun dari akun Google adalah com.google . Selalu batasi tipe akun dengan ID domain untuk domain yang Anda miliki atau Anda kontrol. Hal ini akan memastikan bahwa tipe akun Anda bersifat unik.
|
Tipe akun yang menawarkan data kontak biasanya memiliki adaptor sinkronisasi terkait yang menyinkronkan dengan Penyedia Kontak. |
DELETED
|
Flag "dihapus" untuk kontak mentah. | Flag ini memungkinkan Penyedia Kontak memelihara baris secara internal hingga adaptor sinkronisasi bisa menghapus baris dari server mereka dan akhirnya menghapus baris dari repositori. |
Catatan
Berikut ini adalah catatan penting tentang tabel ContactsContract.RawContacts
:
-
Nama kontak mentah tidak disimpan di barisnya dalam
ContactsContract.RawContacts
. Sebagai gantinya, nama tersebut disimpan dalam tabelContactsContract.Data
, pada barisContactsContract.CommonDataKinds.StructuredName
. Kontak mentah hanya memiliki satu baris dari tipe ini dalam tabelContactsContract.Data
. -
Perhatian: Untuk menggunakan data akun sendiri dalam baris kontak mentah, akun harus didaftarkan lebih dahulu dengan
AccountManager
. Caranya, mintalah pengguna untuk menambahkan tipe akun dan nama akun ke dalam daftar akun. Jika Anda tidak melakukannya, Penyedia Kontak secara otomatis akan menghapus baris kontak mentah Anda.Misalnya, Anda ingin aplikasi mempertahankan data kontak untuk layanan berbasis web dengan domain
com.example.dataservice
, dan akun pengguna untuk layanan Anda adalahbecky.sharp@dataservice.example.com
, pengguna harus menambahkan lebih dahulu "tipe" akun (com.example.dataservice
) dan "nama" akun (becky.smart@dataservice.example.com
) sebelum aplikasi Anda bisa menambahkan baris kontak mentah. Anda bisa menjelaskan ketentuan ini kepada pengguna dalam dokumentasi, atau meminta pengguna untuk menambahkan tipe dan nama, atau keduanya. Tipe akun dan nama akun dijelaskan lebih detail di bagian berikutnya.
Sumber data kontak mentah
Untuk memahami cara kerja kontak mentah, perhatikan pengguna "Emily Dickinson" yang mendefinisikan tiga akun pengguna berikut pada perangkatnya:
emily.dickinson@gmail.com
emilyd@gmail.com
- Akun Twitter "belle_of_amherst"
Pengguna ini telah mengaktifkan Sinkronkan Kontak untuk ketiga akun dalam setelan Akun.
Anggaplah Emily Dickinson membuka jendela browser, login ke Gmail sebagai emily.dickinson@gmail.com
, membuka Kontak, dan menambahkan "Thomas Higginson". Kemudian, ia login ke Gmail sebagai emilyd@gmail.com
dan mengirimkan email kepada "Thomas Higginson", yang akan menambahkan Thomas secara otomatis sebagai kontak. Dia juga mengikuti "colonel_tom" (ID Twitter Thomas Higginson) di Twitter.
Penyedia Kontak membuat tiga kontak mentah akibat pekerjaan ini:
-
Kontak mentah untuk "Thomas Higginson" yang dikaitkan dengan
emily.dickinson@gmail.com
. Tipe akun penggunanya adalah Google. -
Kontak mentah kedua untuk "Thomas Higginson" yang dikaitkan dengan
emilyd@gmail.com
. Tipe akun pengguna juga Google. Ada kontak mentah kedua meskipun nama tersebut identik dengan nama sebelumnya karena orang bersangkutan ditambahkan untuk akun pengguna yang berbeda. - Kontak mentah ketiga untuk "Thomas Higginson" yang dikaitkan dengan "belle_of_amherst". Tipe akun penggunanya adalah Twitter.
Data
Seperti yang telah disebutkan, data untuk kontak mentah disimpan dalam baris ContactsContract.Data
yang ditautkan dengan nilai _ID
kontak mentah. Cara ini memungkinkan satu kontak mentah memiliki beberapa instance tipe data yang sama dengan alamat email atau nomor ponsel. Misalnya, jika "Thomas Higginson" untuk emilyd@gmail.com
(baris kontak mentah untuk Thomas Higginson yang terhubung dengan akun Google emilyd@gmail.com
) memiliki alamat email utama thigg@gmail.com
dan alamat email kerja thomas.higginson@gmail.com
, Penyedia Kontak akan menyimpan dua baris alamat email dan menautkan keduanya ke kontak mentah.
Perhatikan bahwa tipe data yang berbeda disimpan dalam satu tabel ini. Baris nama tampilan, nomor ponsel, email, alamat surat, foto, dan data situs semuanya bisa ditemukan dalam tabel ContactsContract.Data
. Untuk membantu mengelola ini, tabel ContactsContract.Data
memiliki beberapa kolom dengan nama deskriptif, dalam kolom lain dengan nama generik. Konten kolom dengan nama deskriptif memiliki arti yang sama terlepas dari tipe data dalam barisnya, sedangkan materi kolom dengan nama generik memiliki arti yang berbeda-beda sesuai dengan tipe data.
Nama kolom deskriptif
Beberapa contoh nama kolom deskriptif adalah:
-
RAW_CONTACT_ID
-
Nilai kolom
_ID
kontak mentah untuk data ini. -
MIMETYPE
-
Tipe data yang disimpan dalam baris ini, dinyatakan berupa tipe MIME khusus. Penyedia Kontak menggunakan tipe MIME yang didefinisikan dalam subclass
ContactsContract.CommonDataKinds
. Tipe MIME ini adalah open source, dan bisa digunakan oleh setiap aplikasi atau adaptor sinkronisasi yang bisa digunakan bersama Penyedia Kontak. -
IS_PRIMARY
-
Jika tipe baris data ini bisa terjadi lebih dari satu kali untuk suatu kontak mentah, kolom
IS_PRIMARY
menandai baris data yang berisi data utama untuk tipe itu. Misalnya, jika pengguna menekan lama sebuah nomor ponsel untuk sebuah kontak dan memilih Tetapkan default, maka barisContactsContract.Data
yang berisi angka itu mengatur kolomIS_PRIMARY
-nya ke suatu nilai bukan nol.
Nama kolom generik
Ada 15 kolom generik bernama DATA1
hingga DATA15
yang tersedia secara umum dan empat kolom generik tambahan SYNC1
hingga SYNC4
yang harus digunakan hanya oleh adaptor sinkronisasi. Konstanta nama kolom generik selalu berfungsi, terlepas dari tipe data dalam baris.
Kolom DATA1
diindeks. Penyedia Kontak selalu menggunakan kolom ini untuk data yang diharapkan penyedia akan menjadi target yang paling sering dari suatu kueri. Misalnya, dalam baris email, kolom ini berisi alamat email sebenarnya.
Sesuai konvensi, kolom DATA15
dicadangkan untuk menyimpan data Binary Large Object (BLOB) seperti thumbnail foto.
Nama kolom bertipe spesifik
Guna memudahkan pekerjaan dengan kolom untuk tipe baris tertentu, Penyedia Kontak juga menyediakan konstanta nama kolom bertipe spesifik, yang didefinisikan dalam subclass ContactsContract.CommonDataKinds
. Konstanta hanya memberikan nama konstanta yang berbeda ke nama kolom yang sama, yang membantu Anda mengakses data dalam baris bertipe spesifik.
Misalnya, class ContactsContract.CommonDataKinds.Email
mendefinisikan konstanta nama kolom bertipe spesifik untuk baris ContactsContract.Data
yang memiliki tipe MIME Email.CONTENT_ITEM_TYPE
. Class ini berisi konstanta ADDRESS
untuk kolom alamat email. Nilai sesungguhnya dari ADDRESS
adalah "data1", yang sama dengan nama generik kolom.
Perhatian: Jangan tambahkan data khusus Anda sendiri ke tabel ContactsContract.Data
dengan menggunakan baris yang memiliki salah satu tipe MIME yang telah ditetapkan penyedia. Jika melakukannya, Anda bisa kehilangan data atau menyebabkan penyedia gagal berfungsi. Misalnya, Anda seharusnya tidak menambahkan baris bertipe MIME Email.CONTENT_ITEM_TYPE
yang berisi nama pengguna sebagai ganti alamat email dalam kolom DATA1
. Jika Anda menggunakan tipe MIME khusus sendiri untuk baris itu, maka Anda bebas untuk menetapkan nama kolom bertipe spesifik dan menggunakan kolom sesuai keinginan Anda.
Gambar 2 menampilkan bagaimana kolom deskriptif dan kolom data muncul dalam baris ContactsContract.Data
, dan bagaimana nama kolom bertipe spesifik "menempatkan" nama kolom generik

Gambar 2. Nama kolom bertipe khusus dan nama kolom umum.
Kelas nama kolom bertipe spesifik
Tabel 2 berisi daftar kelas nama kolom bertipe spesifik yang paling umum digunakan:
Tabel 2. Kelas nama kolom bertipe khusus
Kelas pemetaan | Tipe data | Catatan |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
Data nama untuk kontak mentah yang dikaitkan dengan baris data ini. | Kontak mentah hanya memiliki salah satu baris ini. |
ContactsContract.CommonDataKinds.Photo |
Foto utama untuk kontak mentah yang dikaitkan dengan baris data ini. | Kontak mentah hanya memiliki salah satu baris ini. |
ContactsContract.CommonDataKinds.Email |
Alamat email untuk kontak mentah yang dikaitkan dengan baris data ini. | Kontak mentah bisa memiliki beberapa alamat email. |
ContactsContract.CommonDataKinds.StructuredPostal |
Alamat pos untuk kontak mentah yang dikaitkan dengan baris data ini. | Kontak mentah bisa memiliki beberapa alamat pos. |
ContactsContract.CommonDataKinds.GroupMembership |
ID yang menautkan kontak mentah ke salah satu grup dalam Penyedia Kontak. | Grup adalah fitur opsional pada tipe akun dan nama akun. Grup dijelaskan lebih detail di bagian Grup kontak. |
Kontak
Penyedia Kontak mengombinasikan baris kontak mentah di semua tipe akun dan nama akun untuk membentuk kontak. Hal ini memudahkan menampilkan dan memodifikasi semua data yang telah dikumpulkan pengguna untuk seseorang. Penyedia Kontak mengelola pembuatan baris kontak baru, dan agregasi kontak mentah dengan baris kontak yang ada. Baik aplikasi maupun adaptor sinkronisasi tidak boleh menambahkan kontak dan sebagian kolom dalam baris kontak yang bersifat hanya baca.
Catatan: Jika Anda mencoba menambahkan kontak ke Penyedia Kontak dengan insert()
, Anda akan mendapatkan pengecualian UnsupportedOperationException
. Jika Anda mencoba memperbarui sebuah kolom yang tercantum sebagai "hanya-baca", pembaruan akan diabaikan.
Penyedia Kontak membuat kontak baru untuk merespons penambahan kontak mentah baru yang tidak cocok dengan kontak yang ada. Penyedia juga melakukan ini jika data kontak mentah yang ada berubah sehingga tidak lagi cocok dengan kontak yang sebelumnya dihubungkan. Jika aplikasi atau adaptor sinkronisasi membuat kontak mentah baru yang memang cocok dengan kontak yang ada, kontak mentah baru akan diagregasikan ke kontak yang ada.
Penyedia Kontak menautkan baris kontak ke baris kontak mentahnya dengan kolom _ID
dari baris kontak dalam tabel Contacts
. Kolom CONTACT_ID
tabel kontak mentah ContactsContract.RawContacts
berisi nilai _ID
untuk baris kontak yang terhubung dengan tiap baris kontak mentah.
Tabel ContactsContract.Contacts
juga memiliki kolom LOOKUP_KEY
yang merupakan tautan "permanen" ke baris kontak. Karena memelihara kontak secara otomatis, Penyedia Kontak bisa mengubah nilai _ID
baris kontak untuk merespons agregasi atau sinkronisasi. Sekalipun ini terjadi, URI konten CONTENT_LOOKUP_URI
yang dikombinasikan dengan LOOKUP_KEY
kontak akan tetap menunjuk ke baris kontak itu, sehingga Anda bisa menggunakan LOOKUP_KEY
untuk memelihara link ke kontak "favorit", dan seterusnya. Kolom ini memiliki formatnya sendiri, yang tidak terkait dengan format kolom _ID
.
Gambar 3 menampilkan cara ketiga tabel utama terkait satu sama lain.

Gambar 3. Hubungan tabel Contacts, Raw Contacts, dan Details.
Perhatian: Jika Anda mempublikasi aplikasi ke Google Play Store, atau jika aplikasi Anda berada di perangkat yang menjalankan Android 10 (level API 29) atau lebih tinggi, perlu diingat bahwa sekumpulan kolom data dan metode kontak yang terbatas sudah usang.
Pada ketentuan yang disebutkan, sistem secara berkala menghapus nilai apa pun yang ditulis ke kolom data ini:
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
API yang digunakan untuk mengatur kolom data di atas juga sudah tidak digunakan lagi.
Selain itu, kolom berikut tidak lagi sering menampilkan kontak. Perhatikan bahwa beberapa kolom ini memengaruhi peringkat kontak hanya jika kontak tersebut merupakan bagian dari jenis data tertentu.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(hanya akan memengaruhi jenis data Email, Ponsel, Dapat Dipanggil, dan Dapat Dihubungi) -
ENTERPRISE_CONTENT_FILTER_URI
(hanya akan memengaruhi jenis data Email, Ponsel, dan Dapat Ditelepon)
Jika aplikasi Anda mengakses dan memperbarui kolom atau API ini, gunakan metode alternatif. Misalnya, Anda dapat memenuhi kasus penggunaan tertentu dengan menggunakan penyedia konten pribadi atau data lain yang disimpan dalam aplikasi atau sistem backend Anda.
Untuk memverifikasi fungsi aplikasi Anda tidak terpengaruh dengan perubahan ini, Anda dapat menghapus kolom data ini secara manual. Untuk melakukannya, jalankan perintah ADB berikut di perangkat yang menjalankan Android 4.1 (API level 16) atau lebih tinggi:
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
Data Dari adaptor sinkronisasi
Pengguna memasukkan data kontak secara langsung ke dalam perangkat, namun data juga masuk ke Penyedia Kontak dari layanan web melalui adaptor sinkronisasi, yang mengotomatiskan transfer data antara perangkat dan layanan. Adaptor sinkronisasi berjalan di latar belakang di bawah kontrol sistem, dan memanggil metode ContentResolver
untuk mengelola data.
Di Android, layanan web yang digunakan adaptor sinkronisasi diidentifikasi melalui tipe akun. Setiap adaptor sinkronisasi bekerja dengan satu tipe akun, tetapi bisa mendukung beberapa nama akun untuk tipe itu. Tipe akun dan nama akun dijelaskan secara singkat di bagian Sumber data kontak mentah. Definisi berikut menyediakan detail selengkapnya, dan menjelaskan cara tipe dan nama akun berkaitan dengan adaptor sinkronisasi dan layanan.
- Tipe akun
-
Mengidentifikasi layanan tempat pengguna menyimpan data. Sering kali, pengguna harus mengautentikasi diri dengan layanan. Misalnya, Google Kontak adalah tipe akun, yang diidentifikasi dengan kode
google.com
. Nilai ini sesuai dengan tipe akun yang digunakan olehAccountManager
. - Nama akun
- Mengidentifikasi akun atau proses login tertentu untuk suatu tipe akun. Akun Google Kontak sama dengan akun Google, yang memiliki alamat email sebagai nama akun. Layanan lain mungkin menggunakan nama pengguna satu-kata atau identitas berupa angka.
Tipe akun tidak harus unik. Pengguna boleh mengonfigurasi beberapa akun Google Kontak dan mendownload data ke Penyedia Kontak; ini mungkin terjadi jika pengguna memiliki satu set kontak pribadi untuk satu nama akun pribadi, dan satu set lagi untuk pekerjaan. Nama akun biasanya unik. Bersama-sama, keduanya mengidentifikasi aliran data tertentu antara Penyedia Kontak dan layanan eksternal.
Jika Anda ingin mentransfer data layanan ke Penyedia Kontak, Anda perlu menulis adaptor sinkronisasi sendiri. Hal ini dijelaskan lebih detail di bagian Adaptor Sinkronisasi Penyedia Kontak.
Gambar 4 menampilkan cara Penyedia Kontak dimasukkan ke dalam aliran data tentang orang. Dalam kotak bertanda "sync adapters", setiap adaptor diberi label menurut tipe akunnya.

Gambar 4. Alur data Penyedia Kontak.
Izin yang diperlukan
Aplikasi yang ingin mengakses Penyedia Kontak harus meminta izin berikut:
- Akses baca ke satu atau beberapa tabel
-
READ_CONTACTS
, yang ditetapkan dalamAndroidManifest.xml
dengan elemen<uses-permission>
sebagai<uses-permission android:name="android.permission.READ_CONTACTS">
. - Akses tulis ke satu atau beberapa tabel
-
WRITE_CONTACTS
, yang ditetapkan dalamAndroidManifest.xml
dengan elemen<uses-permission>
sebagai<uses-permission android:name="android.permission.WRITE_CONTACTS">
.
Izin ini tidak diperluas ke data profil pengguna. Profil pengguna dan izin yang diperlukan dibahas di bagian berikut, Profil Pengguna.
Ingatlah bahwa data kontak pengguna bersifat pribadi dan sensitif. Pengguna mempersoalkan privasinya, sehingga tidak ingin aplikasi mengumpulkan data tentang diri atau kontak mereka. Jika alasan Anda memerlukan izin untuk mengakses data kontak tidak jelas, pengguna mungkin memberi aplikasi Anda peringkat rendah atau langsung menolak menginstalnya.
Profil pengguna
Tabel ContactsContract.Contacts
berisi satu baris yang berisi data profil untuk pengguna perangkat. Data ini menjelaskan user
perangkat bukannya salah satu kontak pengguna. Baris kontak profil ditautkan ke baris kontak mentah untuk setiap sistem yang menggunakan profil. Setiap baris kontak mentah profil bisa memiliki beberapa baris data. Konstanta untuk mengakses profil pengguna tersedia dalam class ContactsContract.Profile
.
Akses ke profil pengguna memerlukan izin khusus. Selain itu, izin READ_CONTACTS
dan WRITE_CONTACTS
diperlukan untuk membaca dan menulis, akses ke profil pengguna memerlukan masing-masing izin android.Manifest.permission#READ_PROFILE dan android.Manifest.permission#WRITE_PROFILE untuk akses baca dan tulis.
Ingatlah bahwa Anda harus mempertimbangkan profil pengguna bersifat sensitif. Izin android.Manifest.permission#READ_PROFILE memungkinkan Anda mengakses data yang mengidentifikasi pengguna perangkat secara pribadi. Pastikan memberi tahu pengguna alasan Anda memerlukan izin akses profil pengguna dalam keterangan aplikasi Anda.
Untuk mengambil baris kontak berisi profil pengguna, panggil ContentResolver.query()
. Atur URI konten ke CONTENT_URI
dan jangan sediakan kriteria pemilihan apa pun. Anda juga bisa menggunakan URI konten ini sebagai URI dasar untuk mengambil kontak mentah atau data untuk profil. Misalnya, cuplikan kode ini mengambil data untuk profil:
Kotlin
// Sets the columns to retrieve for the user profile projection = arrayOf( ContactsContract.Profile._ID, ContactsContract.Profile.DISPLAY_NAME_PRIMARY, ContactsContract.Profile.LOOKUP_KEY, ContactsContract.Profile.PHOTO_THUMBNAIL_URI ) // Retrieves the profile from the Contacts Provider profileCursor = contentResolver.query( ContactsContract.Profile.CONTENT_URI, projection, null, null, null )
Java
// Sets the columns to retrieve for the user profile projection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.LOOKUP_KEY, Profile.PHOTO_THUMBNAIL_URI }; // Retrieves the profile from the Contacts Provider profileCursor = getContentResolver().query( Profile.CONTENT_URI, projection , null, null, null);
Catatan: Jika Anda mengambil beberapa baris kontak, dan ingin menentukan apakah salah satu baris adalah profil pengguna, uji kolom IS_USER_PROFILE
pada baris tersebut. Kolom ini diatur ke "1" jika kontak adalah profil pengguna.
Metadata Penyedia Kontak
Penyedia Kontak mengelola data yang mencatat status data kontak dalam repositori. Metadata repositori ini disimpan di berbagai tempat, termasuk baris-baris tabel Kontak Mentah, Data, dan Kontak, tabel ContactsContract.Settings
, dan tabel ContactsContract.SyncState
. Tabel berikut menampilkan efek setiap potongan metadata ini:
Tabel 3. Metadata di Penyedia Kontak
Tabel | Kolom | Nilai | Arti |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0" - tidak berubah sejak sinkronisasi terakhir. |
Menandai kontak mentah yang berubah pada perangkat dan telah disinkronkan kembali ke server. Nilai diatur secara otomatis oleh Penyedia Kontak bila aplikasi Android memperbarui baris.
Adaptor sinkronisasi yang memodifikasi kontak mentah atau tabel data harus selalu menambahkan string |
"1" - berubah sejak sinkronisasi terakhir, harus disinkronkan kembali ke server. | |||
ContactsContract.RawContacts |
VERSION |
Nomor versi baris ini. | Penyedia Kontak menambahkan nilai ini secara otomatis bila baris atau data terkaitnya berubah. |
ContactsContract.Data |
DATA_VERSION |
Nomor versi baris ini. | Penyedia Kontak menambahkan nilai ini secara otomatis bila baris data berubah. |
ContactsContract.RawContacts |
SOURCE_ID |
Nilai string yang mengidentifikasi secara unik kontak mentah ini ke akun tempat kontak dibuat. |
Bila adaptor sinkronisasi membuat kontak mentah baru, kolom ini harus disetel ke ID unik server untuk kontak mentah tersebut. Bila aplikasi Android membuat kontak mentah baru, aplikasi harus membiarkan kolom ini kosong. Ini mengisyaratkan pada adaptor sinkronisasi bahwa adaptor harus membuat kontak mentah baru pada server, dan mendapatkan nilai untuk SOURCE_ID .
Khususnya, id sumber harus unik untuk setiap tipe akun dan stabil di semua sinkronisasi:
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0" - Kontak dalam grup ini tidak boleh terlihat dalam UI aplikasi Android. | Kolom ini digunakan untuk kompatibilitas dengan server yang memungkinkan pengguna menyembunyikan kontak dalam grup tertentu. |
"1" - Kontak dalam grup ini boleh terlihat dalam UI aplikasi. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0" - Untuk akun dan tipe akun ini, kontak yang tidak termasuk dalam suatu grup tidak akan terlihat pada UI aplikasi Android. |
Secara default, kontak tidak terlihat jika tidak satu pun kontak mentahnya termasuk dalam suatu grup (Keanggotaan grup untuk kontak mentah ditandai oleh satu atau beberapa baris ContactsContract.CommonDataKinds.GroupMembership dalam tabel ContactsContract.Data ). Dengan menyetel flag ini dalam baris tabel ContactsContract.Settings untuk tipe akun dan akun, Anda bisa memaksakan kontak tanpa grup agar terlihat. Satu kegunaan flag ini adalah menampilkan kontak dari server yang tidak menggunakan grup.
|
"1" - Untuk akun dan tipe akun ini, kontak yang tidak termasuk dalam suatu grup akan terlihat pada UI aplikasi. | |||
ContactsContract.SyncState |
(semua) | Gunakan tabel ini untuk menyimpan metadata bagi adaptor sinkronisasi Anda. | Dengan tabel ini, Anda bisa menyimpan status sinkronisasi dan data lain yang terkait dengan sinkronisasi secara persisten pada perangkat. |
Akses Penyedia Kontak
Bagian ini menjelaskan panduan untuk mengakses data dari Penyedia Kontak, yang berfokus pada hal-hal berikut:
- Kueri entitas.
- Modifikasi batch.
- Pengambilan dan modifikasi dengan maksud.
- Integritas data.
Membuat modifikasi dari adaptor sinkronisasi juga secara lebih detail di bagian adaptor sinkronisasi Penyedia Kontak.
Untuk contoh tentang cara menggunakan Penyedia Kontak saat mencari kontak, lihat contoh Dasar Dapat Dihubungi.
Membuat kueri entitas
Karena disusun secara hierarki, tabel-tabel Penyedia Kontak sering kali berguna untuk mengambil baris dan semua baris "turunan" yang ditautkan dengannya. Misalnya, untuk menampilkan semua informasi untuk satu orang, Anda mungkin ingin mengambil semua baris ContactsContract.RawContacts
untuk satu baris ContactsContract.Contacts
, atau semua baris ContactsContract.CommonDataKinds.Email
untuk satu baris ContactsContract.RawContacts
. Untuk memudahkan hal ini, Penyedia Kontak menawarkan konstruksi entitas, yang berfungsi seperti gabungan database di antara tabel-tabel.
Entitas adalah seperti tabel yang terdiri atas kolom-kolom terpilih dari tabel induk dan tabel turunannya. Bila melakukan kueri sebuah entitas, Anda memberikan proyeksi dan kriteria penelusuran berdasarkan kolom-kolom yang tersedia dari entitas itu. Hasilnya adalah sebuah Cursor
yang berisi satu baris untuk setiap baris tabel turunan yang diambil. Misalnya, jika Anda melakukan kueri ContactsContract.Contacts.Entity
untuk satu nama kontak dan semua baris ContactsContract.CommonDataKinds.Email
untuk semua kontak mentah bagi nama itu, Anda akan mendapatkan kembali Cursor
berisi satu baris untuk setiap baris ContactsContract.CommonDataKinds.Email
.
Entitas menyederhanakan kueri. Dengan menggunakan entitas, Anda bisa mengambil semua data kontak untuk satu kontak atau kontak mentah sekaligus, sebagai ganti harus melakukan kueri tabel induk terlebih dahulu untuk mendapatkan ID, lalu harus melakukan kueri tabel turunan dengan ID itu. Selain itu, Penyedia Kontak akan memproses kueri terhadap entitas dalam satu transaksi, yang memastikan bahwa data yang diambil konsisten secara internal.
Catatan: Entitas biasanya tidak berisi semua kolom tabel induk dan turunan. Jika Anda mencoba menggunakan nama kolom yang tidak ada dalam daftar konstanta nama kolom untuk entitas, Anda akan mendapatkan Exception
.
Cuplikan berikut menampilkan cara mengambil semua baris kontak mentah untuk sebuah kontak. Cuplikan ini adalah bagian dari aplikasi lebih besar yang memiliki dua aktivitas, "utama" dan "detail". Aktivitas utama menampilkan daftar baris kontak; bila pengguna memilih satu baris, aktivitas akan mengirimkan ID-nya ke aktivitas detail. Aktivitas detail menggunakan ContactsContract.Contacts.Entity
untuk menampilkan semua baris data dari semua kontak mentah yang dikaitkan dengan kontak terpilih.
Cuplikan ini diambil dari aktivitas "detail":
Kotlin
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY ) // Initializes the loader identified by LOADER_ID. loaderManager.initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this // The context of the activity ) // Creates a new cursor adapter to attach to the list view cursorAdapter = SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0) // flags // Sets the ListView's backing adapter. rawContactList.adapter = cursorAdapter ... override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ val projection: Array<String> = arrayOf( ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE ) /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC" /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return CursorLoader( applicationContext, // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder // Sort by the raw contact ID. ) }
Java
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY); // Initializes the loader identified by LOADER_ID. getLoaderManager().initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this); // The context of the activity // Creates a new cursor adapter to attach to the list view cursorAdapter = new SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0); // flags // Sets the ListView's backing adapter. rawContactList.setAdapter(cursorAdapter); ... @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ String[] projection = { ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE }; /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ String sortOrder = ContactsContract.Contacts.Entity.RAW_CONTACT_ID + " ASC"; /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return new CursorLoader( getApplicationContext(), // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder); // Sort by the raw contact ID. }
Bila selesai dimuat, LoaderManager
akan memicu callback ke onLoadFinished()
. Salah satu argumen masuk pada metode ini adalah Cursor
bersama hasil kueri. Dalam aplikasi Anda sendiri, Anda bisa memperoleh data dari Cursor
ini untuk menampilkannya atau menggunakannya lebih jauh.
Modifikasi batch
Bila memungkinkan, Anda harus menyisipkan, memperbarui, dan menghapus data dalam Penyedia Kontak dengan "mode batch", dengan membuat ArrayList
dari objek-objek ContentProviderOperation
dan memanggil applyBatch()
. Karena Penyedia Kontak menjalankan semua operasi dalam satu applyBatch()
transaksi, modifikasi Anda tidak akan pernah meninggalkan repositori kontak dalam status tidak konsisten. Modifikasi batch juga memudahkan penyisipan kontak mentah dan data detailnya sekaligus.
Catatan: Untuk memodifikasi satu kontak mentah, pertimbangkan untuk mengirim intent ke aplikasi kontak perangkat daripada menangani modifikasi dalam aplikasi Anda. Cara ini dijelaskan lebih detail di bagian Pengambilan dan modifikasi dengan intent.
Yield point
Modifikasi batch yang berisi operasi dalam jumlah besar bisa memblokir proses lain, yang mengakibatkan pengalaman pengguna yang buruk secara keseluruhan. Untuk menata semua modifikasi yang ingin Anda jalankan dalam sesedikit mungkin daftar terpisah, sambil mencegah modifikasi dari memblokir sistem, Anda harus menetapkan yield point untuk satu atau beberapa operasi. Yield point adalah objek ContentProviderOperation
yang mengatur nilai isYieldAllowed()
-nya ke true
. Bila menemui yield point, Penyedia Kontak akan menghentikan pekerjaannya untuk membiarkan proses lain berjalan dan menutup transaksi saat ini. Bila dimulai lagi, penyedia akan melanjutkan dengan operasi berikutnya di ArrayList
dan memulai transaksi baru.
Yield point memang menyebabkan lebih dari satu transaksi per panggilan ke applyBatch()
. Karena itu, Anda harus menetapkan yield point pada operasi terakhir untuk satu set baris terkait. Misalnya, Anda harus menetapkan yield point pada operasi terakhir di satu set yang menambahkan baris kontak mentah dan baris data terkait, atau operasi terakhir untuk satu set baris yang terkait dengan satu kontak.
Yield point juga merupakan unit operasi atomis. Semua akses antara dua yield point bisa saja berhasil atau gagal sebagai satu unit. Jika Anda mengatur yield point, operasi atomis terkecil adalah seluruh batch operasi. Jika menggunakan yield point, Anda akan mencegah operasi menurunkan kinerja sistem, sekaligus memastikan subset operasi bersifat atomis.
Acuan balik modifikasi
Saat Anda menyisipkan baris kontak mentah baru dan baris data terkaitnya sebagai satu set objek ContentProviderOperation
, Anda harus menautkan baris data ke baris kontak mentah dengan memasukkan nilai _ID
kontak mentah sebagai nilai RAW_CONTACT_ID
. Akan tetapi, nilai ini tidak tersedia saat Anda membuat ContentProviderOperation
untuk baris data, karena Anda belum menerapkan ContentProviderOperation
untuk baris kontak mentah. Solusinya, class ContentProviderOperation.Builder
memiliki metode withValueBackReference()
. Metode ini memungkinkan Anda menyisipkan atau mengubah kolom dengan hasil dari operasi sebelumnya.
Metode withValueBackReference()
memiliki dua argumen:
-
key
- Kunci dari pasangan kunci-nilai. Nilai argumen ini harus berupa nama kolom dalam tabel yang Anda modifikasi.
-
previousResult
-
Indeks berbasis 0 dari nilai pada array objek
ContentProviderResult
dariapplyBatch()
. Saat operasi batch diterapkan, hasil tiap operasi akan disimpan dalam array hasil antara. NilaipreviousResult
adalah indeks dari salah satu hasil ini, yang diambil dan disimpan bersama nilaikey
. Cara ini memungkinkan Anda menyisipkan catatan kontak mentah baru dan mendapatkan kembali nilai_ID
-nya, lalu membuat "acuan balik" ke nilai itu saat Anda menambahkan barisContactsContract.Data
.Seluruh array hasil dibuat saat Anda memanggil
applyBatch()
untuk pertama kali, dengan ukuran setara dengan ukuranArrayList
dari objekContentProviderOperation
yang Anda sediakan. Akan tetapi, semua elemen dalam array hasil diatur kenull
, dan jika Anda mencoba melakukan acuan balik ke hasil untuk operasi yang belum diterapkan,withValueBackReference()
akan mengeluarkanException
.
Cuplikan kode berikut menampilkan cara menyisipkan kontak mentah baru dan data secara batch. Cuplikan kode ini menyertakan kode yang menetapkan yield point dan menggunakan acuan balik.
Cuplikan pertama mengambil data kontak dari UI. Pada saat ini, pengguna sudah memilih akun tempat kontak mentah baru harus ditambahkan.
Kotlin
// Creates a contact entry from the current UI values, using the currently-selected account. private fun createContactEntry() { /* * Gets values from the UI */ val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition] val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]
Java
// Creates a contact entry from the current UI values, using the currently-selected account. protected void createContactEntry() { /* * Gets values from the UI */ String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); int phoneType = contactPhoneTypes.get( contactPhoneTypeSpinner.getSelectedItemPosition()); int emailType = contactEmailTypes.get( contactEmailTypeSpinner.getSelectedItemPosition());
Cuplikan berikutnya membuat operasi untuk menyisipkan baris kontak mentah ke dalam tabel ContactsContract.RawContacts
:
Kotlin
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. val ops = arrayListOf<ContentProviderOperation>() /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ var op: ContentProviderOperation.Builder = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type) // Builds the operation and adds it to the array of operations ops.add(op.build())
Java
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Builds the operation and adds it to the array of operations ops.add(op.build());
Berikutnya, kode akan membuat baris data untuk baris-baris nama tampilan, ponsel, dan email.
Setiap objek pembangun operasi menggunakan withValueBackReference()
untuk mendapatkan RAW_CONTACT_ID
. Acuan menunjuk balik ke objek ContentProviderResult
dari operasi pertama, yang menambahkan baris kontak mentah dan menampilkan nilai _ID
barunya. Hasilnya, setiap data ditautkan secara otomatis oleh RAW_CONTACT_ID
-nya ke baris ContactsContract.RawContacts
baru yang memilikinya.
Objek ContentProviderOperation.Builder
yang menambahkan baris email ditandai dengan withYieldAllowed()
, yang menyetel yield point:
Kotlin
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType) /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true) // Builds the operation and adds it to the array of operations ops.add(op.build())
Java
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType); /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true); // Builds the operation and adds it to the array of operations ops.add(op.build());
Cuplikan terakhir menampilkan panggilan ke applyBatch()
yang menyisipkan baris-baris kontak mentah dan data baru.
Kotlin
// Ask the Contacts Provider to create a new contact Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})") Log.d(TAG, "Creating contact: $name") /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) } catch (e: Exception) { // Display a warning val txt: String = getString(R.string.contactCreationFailure) Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show() // Log exception Log.e(TAG, "Exception encountered while inserting contact: $e") } }
Java
// Ask the Contacts Provider to create a new contact Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" + selectedAccount.getType() + ")"); Log.d(TAG,"Creating contact: " + name); /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { // Display a warning Context ctx = getApplicationContext(); CharSequence txt = getString(R.string.contactCreationFailure); int duration = Toast.LENGTH_SHORT; Toast toast = Toast.makeText(ctx, txt, duration); toast.show(); // Log exception Log.e(TAG, "Exception encountered while inserting contact: " + e); } }
Operasi batch juga memungkinkan Anda menerapkan kontrol serentak optimistis, sebuah metode yang menerapkan transaksi modifikasi tanpa harus mengunci repositori yang mendasari. Untuk menggunakan metode ini, terapkan transaksi dan periksa modifikasi lain yang mungkin telah dibuat bersamaan. Jika ternyata modifikasi tidak konsisten, Anda mengembalikan transaksi ke kondisi semula dan mencobanya kembali.
Kontrol konkurensi optimistis berguna untuk perangkat seluler, apabila hanya ada satu pengguna setiap kalinya, dan akses simultan ke repositori data jarang terjadi. Karena penguncian tidak digunakan, tidak ada waktu yang terbuang untuk memasang kunci atau menunggu transaksi lain untuk melepas kunci.
Untuk menggunakan kontrol konkurensi optimistis saat memperbarui satu baris ContactsContract.RawContacts
, ikuti langkah-langkah ini:
-
Ambil kolom
VERSION
kontak mentah bersama data lain yang Anda ambil. -
Buat sebuah objek
ContentProviderOperation.Builder
yang cocok untuk memberlakukan batasan, dengan menggunakan metodenewAssertQuery(Uri)
. Untuk URI materi, gunakanRawContacts.CONTENT_URI
dengan_ID
kontak mentah yang ditambahkan padanya. -
Untuk objek
ContentProviderOperation.Builder
, panggilwithValue()
untuk membandingkan kolomVERSION
dengan nomor versi yang baru saja Anda ambil. -
Untuk
ContentProviderOperation.Builder
yang sama, panggilwithExpectedCount()
untuk memastikan bahwa hanya satu baris yang diuji oleh pernyataan ini. -
Panggil
build()
untuk membuat objekContentProviderOperation
, kemudian tambahkan objek ini sebagai objek pertama diArrayList
yang Anda teruskan keapplyBatch()
. - Terapkan transaksi batch.
Jika baris kontak mentah diperbarui oleh operasi lain antara waktu Anda membaca baris dan waktu Anda mencoba memodifikasinya, "asert" ContentProviderOperation
akan gagal, dan seluruh batch operasi akan dibatalkan. Anda nanti bisa memilih untuk mencoba ulang batch atau melakukan aksi lain.
Cuplikan berikut memperagakan cara membuat "asert" ContentProviderOperation
setelah melakukan kueri satu kontak mentah yang menggunakan CursorLoader
:
Kotlin
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)) mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)) } ... // Sets up a Uri for the assert operation val rawContactUri: Uri = ContentUris.withAppendedId( ContactsContract.RawContacts.CONTENT_URI, rawContactID ) // Creates a builder for the assert operation val assertOp: ContentProviderOperation.Builder = ContentProviderOperation.newAssertQuery(rawContactUri).apply { // Adds the assertions to the assert operation: checks the version withValue(SyncColumns.VERSION, mVersion) // and count of rows tested withExpectedCount(1) } // Creates an ArrayList to hold the ContentProviderOperation objects val ops = arrayListOf<ContentProviderOperation>() ops.add(assertOp.build()) // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops) } catch (e: OperationApplicationException) { // Actions you want to take if the assert operation fails go here }
Java
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)); mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)); } ... // Sets up a Uri for the assert operation Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID); // Creates a builder for the assert operation ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri); // Adds the assertions to the assert operation: checks the version and count of rows tested assertOp.withValue(SyncColumns.VERSION, mVersion); assertOp.withExpectedCount(1); // Creates an ArrayList to hold the ContentProviderOperation objects ArrayList ops = new ArrayList<ContentProviderOperation>; ops.add(assertOp.build()); // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { ContentProviderResult[] results = getContentResolver().applyBatch(AUTHORITY, ops); } catch (OperationApplicationException e) { // Actions you want to take if the assert operation fails go here }
Pengambilan dan modifikasi dengan intent
Mengirimkan intent ke aplikasi kontak perangkat memungkinkan Anda mengakses Penyedia Kontak secara tidak langsung. Intent akan memulai UI aplikasi kontak perangkat, tempat pengguna bisa melakukan pekerjaan yang terkait dengan kontak. Dengan tipe akses ini, pengguna bisa:
- Memilih kontak dari daftar dan meneruskannya ke aplikasi untuk pekerjaan lebih jauh.
- Mengedit data kontak yang ada.
- Memasukkan kontak mentah baru untuk akun mereka.
- Menghapus kontak atau data kontak.
Jika pengguna menyisipkan atau memperbarui data, Anda bisa mengumpulkan data lebih dahulu dan mengirimkannya sebagai bagian dari intent.
Bila Anda menggunakan intent untuk mengakses Penyedia Kontak melalui aplikasi kontak perangkat, Anda tidak perlu menulis UI atau kode sendiri untuk mengakses penyedia. Anda juga tidak harus meminta izin untuk membaca dari atau menulis ke penyedia. Aplikasi kontak perangkat bisa mendelegasikan izin membaca untuk kontak kepada Anda, dan karena Anda membuat modifikasi pada penyedia melalui aplikasi lain, Anda tidak perlu memiliki izin menulis.
Proses umum pengiriman intent untuk mengakses penyedia dijelaskan secara detail dalam panduan Dasar-Dasar Penyedia Materi di bagian "Akses data melalui intent". Aksi, tipe MIME, dan nilai data yang Anda gunakan untuk tugas yang tersedia dirangkum dalam Tabel 4, sedangkan nilai tambahan yang bisa Anda gunakan bersama putExtra()
tercantum dalam dokumentasi acuan untuk ContactsContract.Intents.Insert
:
Tabel 4. Intent Penyedia Kontak.
Tugas | Aksi | Data | Tipe MIME | Catatan |
---|---|---|---|---|
Memilih kontak dari daftar | ACTION_PICK |
Salah satu dari:
|
Tidak digunakan |
Menampilkan daftar kontak mentah atau daftar data dari kontak mentah, sesuai dengan tipe URI materi yang Anda sediakan.
Panggil |
Menyisipkan kontak mentah baru | Insert.ACTION |
N/A |
RawContacts.CONTENT_TYPE , tipe MIME untuk satu set kontak mentah.
|
Menampilkan layar Add Contact aplikasi kontak perangkat. Nilai tambahan yang Anda tambahkan ke intent akan ditampilkan. Jika dikirimkan bersama startActivityForResult() , URI materi dari kontak mentah yang baru saja ditambahkan akan dikembalikan ke onActivityResult() metode callback aktivitas Anda pada argumen Intent , di kolom "data". Untuk mendapatkan nilainya, panggil getData() .
|
Edit a contact | ACTION_EDIT |
CONTENT_LOOKUP_URI untuk kontak. Aktivitas editor memungkinkan pengguna mengedit setiap data yang dikaitkan dengan kontak ini.
|
Contacts.CONTENT_ITEM_TYPE , kontak tunggal. |
Menampilkan layar Edit Contact dalam aplikasi kontak. Nilai tambahan yang Anda tambahkan ke intent akan ditampilkan. Bila pengguna mengklik Selesai untuk menyimpan hasil edit, aktivitas Anda kembali ke latar depan. |
Menampilkan picker yang juga bisa menambahkan data. | ACTION_INSERT_OR_EDIT |
N/A |
CONTENT_ITEM_TYPE
|
Intent ini selalu menampilkan layar picker aplikasi kontak. Pengguna bisa memilih kontak untuk diedit, atau menambahkan kontak baru. Layar edit atau layar tambah akan muncul, sesuai dengan pilihan pengguna, dan data tambahan yang Anda kirimkan dalam intent akan ditampilkan. Jika aplikasi Anda menampilkan data kontak seperti email atau nomor ponsel, gunakan intent ini agar memungkinkan pengguna menambahkan data ke kontak yang ada.
Catatan: Tidak perlu mengirimkan nilai nama dalam tambahan intent ini, karena pengguna selalu mengambil nama yang ada atau menambahkan nama baru. Selain itu, jika Anda mengirimkan nama, dan pengguna memilih untuk melakukan edit, aplikasi kontak akan menampilkan nama yang Anda kirimkan, yang menimpa nilai sebelumnya. Jika pengguna tidak menyadari hal ini dan menyimpan hasil edit, nilai lama akan hilang. |
Aplikasi kontak perangkat tidak memperbolehkan Anda menghapus kontak mentah atau datanya dengan intent. Sebagai gantinya, untuk menghapus kontak mentah, gunakan ContentResolver.delete()
atau ContentProviderOperation.newDelete()
.
Cuplikan berikut menampilkan cara menyusun dan mengirimkan intent yang menyisipkan kontak dan data mentah baru:
Kotlin
// Gets values from the UI val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val company = companyName.text.toString() val jobtitle = jobTitle.text.toString() /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row val contactData = arrayListOf<ContentValues>() /* * Defines the raw contact row */ // Sets up the row as a ContentValues object val rawContactRow = ContentValues().apply { // Adds the account type and name to the row put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type) put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name) } // Adds the row to the array contactData.add(rawContactRow) /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object val phoneRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Adds the phone number and its type to the row put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) } // Adds the row to the array contactData.add(phoneRow) /* * Sets up the email data row */ // Sets up the row as a ContentValues object val emailRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Adds the email address and its type to the row put(ContactsContract.CommonDataKinds.Email.ADDRESS, email) } // Adds the row to the array contactData.add(emailRow) // Creates a new intent for sending to the device's contacts application val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply { // Sets the MIME type to the one expected by the insertion activity type = ContactsContract.RawContacts.CONTENT_TYPE // Sets the new contact name putExtra(ContactsContract.Intents.Insert.NAME, name) // Sets the new company and job title putExtra(ContactsContract.Intents.Insert.COMPANY, company) putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle) /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData) } // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent)
Java
// Gets values from the UI String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); String company = companyName.getText().toString(); String jobtitle = jobTitle.getText().toString(); // Creates a new intent for sending to the device's contacts application Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION); // Sets the MIME type to the one expected by the insertion activity insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE); // Sets the new contact name insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name); // Sets the new company and job title insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company); insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle); /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row ArrayList<ContentValues> contactData = new ArrayList<ContentValues>(); /* * Defines the raw contact row */ // Sets up the row as a ContentValues object ContentValues rawContactRow = new ContentValues(); // Adds the account type and name to the row rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()); rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Adds the row to the array contactData.add(rawContactRow); /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object ContentValues phoneRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) phoneRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ); // Adds the phone number and its type to the row phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); // Adds the row to the array contactData.add(phoneRow); /* * Sets up the email data row */ // Sets up the row as a ContentValues object ContentValues emailRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) emailRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ); // Adds the email address and its type to the row emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email); // Adds the row to the array contactData.add(emailRow); /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent);
Integritas data
Karena repositori kontak berisi data penting dan sensitif yang diharapkan pengguna agar benar dan terbaru. Penyedia Kontak memiliki aturan yang didefinisikan dengan baik demi integritas data. Anda bertanggung jawab untuk mematuhi aturan ini saat memodifikasi data kontak. Aturan-aturan penting itu dicantumkan di sini:
-
Selalu tambahkan baris
ContactsContract.CommonDataKinds.StructuredName
untuk setiap barisContactsContract.RawContacts
yang Anda tambahkan. -
Baris
ContactsContract.RawContacts
tanpa barisContactsContract.CommonDataKinds.StructuredName
dalam tabelContactsContract.Data
bisa menyebabkan masalah selama agregasi. -
Selalu tautkan baris
ContactsContract.Data
baru ke barisContactsContract.RawContacts
induknya. -
Baris
ContactsContract.Data
yang tidak ditautkan keContactsContract.RawContacts
tidak akan terlihat dalam aplikasi kontak perangkat, dan itu bisa menimbulkan masalah dengan adaptor sinkronisasi. - Ubah data hanya untuk kontak mentah yang Anda miliki.
- Ingatlah bahwa Penyedia Kontak biasanya mengelola data dari berbagai tipe akun/layanan online. Anda harus memastikan bahwa aplikasi Anda hanya memodifikasi atau menghapus data untuk baris milik Anda, dan bahwa aplikasi hanya menyisipkan data dengan tipe akun dan nama yang Anda kontrol.
-
Selalu gunakan konstanta yang didefinisikan dalam
ContactsContract
dan subcalss-nya untuk otoritas, URI materi, URI jalur, nama kolom, tipe MIME, dan nilaiTYPE
. - Menggunakan konstanta ini membantu Anda menghindari kesalahan. Anda juga akan diberi tahu dengan peringatan compiler jika salah satu konstanta sudah tidak digunakan lagi.
Baris data khusus
Dengan membuat dan menggunakan tipe MIME khusus sendiri, Anda bisa menyisipkan, mengedit, menghapus, dan mengambil baris data sendiri dalam tabel ContactsContract.Data
. Baris Anda dibatasi untuk menggunakan kolom yang didefinisikan dalam ContactsContract.DataColumns
, meskipun Anda bisa memetakan nama kolom bertipe spesifik sendiri ke nama kolom default. Dalam aplikasi kontak perangkat, data untuk baris Anda ditampilkan, tetapi tidak bisa diedit atau dihapus, dan pengguna tidak bisa menambahkan data lain. Untuk memudahkan pengguna mengubah baris data khusus Anda, Anda harus menyediakan aktivitas editor dalam aplikasi Anda sendiri.
Untuk menampilkan data khusus, sediakan file contacts.xml
berisi elemen <ContactsAccountType>
dan satu atau beberapa elemen turunan <ContactsDataKind>
. Hal ini dijelaskan lebih detail di bagian <ContactsDataKind> element
.
Untuk mengetahui selengkapnya tentang tipe MIME khusus, bacalah panduan Membuat Penyedia Materi.
Adaptor sinkronisasi Penyedia Kontak
Penyedia Kontak didesain khusus untuk menangani sinkronisasi data kontak antara perangkat dan layanan online. Hal ini memungkinkan pengguna mendownload data yang ada dari perangkat baru dan mengupload data yang ada ke akun baru. Sinkronisasi juga memastikan bahwa pengguna memiliki data terbaru, apa pun sumber penambahan dan perubahan itu. Keuntungan lain dari sinkronisasi adalah membuat data kontak tersedia sekalipun perangkat tidak terhubung ke jaringan.
Walaupun Anda bisa menerapkan sinkronisasi dengan berbagai cara, sistem Android menyediakan kerangka kerja sinkronisasi plugin yang mengotomatiskan tugas-tugas berikut:
- Memeriksa ketersediaan jaringan.
- Menjadwalkan dan menjalankan sinkronisasi, berdasarkan preferensi pengguna.
- Memulai kembali sinkronisasi yang telah berhenti.
Untuk menggunakan kerangka kerja ini, Anda harus menyediakan plugin adaptor sinkronisasi. Setiap adaptor sinkronisasi bersifat unik bagi layanan dan penyedia materi, tetapi mampu menangani beberapa nama akun untuk layanan yang sama. Kerangka kerja ini juga memungkinkan beberapa adaptor sinkronisasi untuk layanan dan penyedia yang sama.
Class dan file adaptor sinkronisasi
Anda mengimplementasikan adaptor sinkronisasi sebagai subclass AbstractThreadedSyncAdapter
dan menginstalnya sebagai bagian dari aplikasi Android. Sistem akan mempelajari adaptor sinkronisasi dari elemen-elemen di manifes aplikasi Anda dan dari file XML khusus yang ditunjuk oleh manifes. File XML menetapkan tipe akun untuk layanan online dan otoritas untuk penyedia materi, yang bersama-sama mengidentifikasi adaptor secara unik. Adaptor sinkronisasi tidak menjadi aktif hingga pengguna menambahkan akun untuk tipe akun adaptor sinkronisasi dan memungkinkan sinkronisasi untuk penyedia materi yang disinkronkan dengan adaptor sinkronisasi. Pada saat itu, sistem mulai mengelola adaptor, memanggil adapter diperlukan untuk menyinkronkan antara penyedia materi dan server.
Catatan: Menggunakan tipe akun sebagai bagian dari identifikasi adaptor sinkronisasi memungkinkan sistem mendeteksi dan mengelompokkan adaptor sinkronisasi yang mengakses berbagai layanan dari organisasi yang sama. Misalnya, adaptor sinkronisasi untuk semua layanan online Google semuanya memiliki tipe akun yang sama com.google
. Bila pengguna menambahkan akun Google ke perangkatnya, semua adaptor sinkronisasi yang terinstal untuk layanan Google akan dicantumkan bersama; setiap adaptor sinkronisasi yang tercantum akan menyinkronkan diri dengan berbagai penyedia materi pada perangkat.
Karena sebagian besar layanan mengharuskan pengguna untuk memeriksa identitas sebelum mengakses data, sistem Android menawarkan kerangka kerja autentikasi yang serupa dengan, dan sering kali digunakan bersama, kerangka kerja adaptor sinkronisasi. Kerangka kerja autentikasi menggunakan autentikator plugin yang merupakan subclass AbstractAccountAuthenticator
. Autentikator memeriksa identitas pengguna dalam langkah-langkah berikut:
- Mengumpulkan nama pengguna, kata sandi, atau informasi serupa (kredensial pengguna).
- Mengirimkan kredensial ke layanan
- Memeriksa balasan layanan.
Jika layanan menerima kredensial, autentikator bisa menyimpan kredensial itu untuk digunakan nanti. Karena kerangka kerja autentikator plugin, AccountManager
bisa menyediakan akses ke setiap token autentikasi yang didukung suatu autentikator dan memilih untuk mengekspos, seperti token autentikasi OAuth2.
Meskipun autentikasi tidak diharuskan, sebagian besar layanan kontak menggunakannya. Akan tetapi, Anda tidak wajib menggunakan kerangka kerja autentikasi Android untuk melakukan autentikasi.
Implementasi adaptor sinkronisasi
Untuk mengimplementasikan adaptor sinkronisasi bagi Penyedia Kontak, Anda perlu memulai dengan membuat aplikasi Android yang berisi elemen-elemen berikut:
-
Komponen
Service
yang merespons permintaan sistem untuk mengikat ke adaptor sinkronisasi. -
Bila sistem ingin menjalankan sinkronisasi, sistem akan memanggil metode
onBind()
layanan untuk mendapatkanIBinder
bagi adaptor sinkronisasi. Hal ini memungkinkan sistem melakukan panggilan lintas proses ke metode adaptor. -
Adaptor sinkronisasi yang sesungguhnya, diimplementasikan sebagai subclass konkret dari
AbstractThreadedSyncAdapter
. -
Class ini melakukan pekerjaan mendownload data dari server, mengupload data ke perangkat, dan menyelesaikan konflik. Pekerjaan utama adaptor diselesaikan dengan metode
onPerformSync()
. Instance class ini harus dibuat sebagai singleton. -
Subclass
Application
. -
Class ini berfungsi sebagai pabrik untuk singleton adaptor sinkronisasi. Gunakan metode
onCreate()
untuk membuat instance adaptor sinkronisasi, dan memberikan metode "getter" statis untuk menampilkan singleton ke metodeonBind()
dari layanan adaptor sinkronisasi. -
Opsional: Komponen
Service
yang merespons permintaan dari sistem untuk autentikasi pengguna. -
AccountManager
memulai layanan ini untuk memulai proses autentikasi. MetodeonCreate()
layanan membuat instance objek autentikator. Bila sistem ingin mengautentikasi akun pengguna untuk adaptor sinkronisasi aplikasi, sistem akan memanggil metodeonBind()
layanan guna mendapatkanIBinder
bagi autentikator. Hal ini memungkinkan sistem melakukan panggilan lintas proses ke metode autentikator.. -
Opsional: Subclass konkret
AbstractAccountAuthenticator
yang menangani permintaan autentikasi. -
Class ini menyediakan metode yang dipicu
AccountManager
untuk mengautentikasi kredensial pengguna dengan server. Detail proses autentikasi sangat bervariasi, berdasarkan teknologi server yang digunakan. Anda harus mengacu ke dokumentasi bagi software server untuk mengetahui selengkapnya tentang autentikasi. - File XML yang mendefinisikan adaptor sinkronisasi dan autentikator bagi sistem.
-
Komponen-komponen layanan adaptor sinkronisasi dan autentikator sebelumnya didefinisikan dalam elemen-elemen
<service>
di manifes aplikasi. Elemen-elemen ini berisi<meta-data>
elemen turunan yang menyediakan data tertentu ke sistem:-
Elemen
<meta-data>
untuk layanan adaptor sinkronisasi menunjuk ke file XMLres/xml/syncadapter.xml
. Pada gilirannya, file ini menetapkan URI untuk layanan web yang akan disinkronkan dengan Penyedia Kontak, dan tipe akun untuk layanan web. -
Opsional: Elemen
<meta-data>
untuk autentikator menunjuk ke file XMLres/xml/authenticator.xml
. Pada gilirannya, file ini menetapkan tipe akun yang didukung autentikator, serta resource UI yang muncul selama proses autentikasi. Tipe akun yang ditetapkan dalam elemen ini harus sama dengan tipe akun yang ditetapkan untuk adaptor sinkronisasi.
-
Elemen
Data aliran sosial
Tabel android.provider.ContactsContract.StreamItems dan android.provider.ContactsContract.StreamItemPhotos mengelola data yang masuk dari jaringan sosial. Anda bisa menulis adaptor sinkronisasi yang menambahkan data aliran dari jaringan Anda sendiri ke tabel-tabel ini, atau Anda bisa membaca data aliran dari tabel-tabel ini dan menampilkannya dalam aplikasi sendiri, atau keduanya. Dengan fitur-fitur ini, layanan dan aplikasi jaringan sosial Anda bisa diintegrasikan ke dalam pengalaman jaringan sosial Android.
Teks aliran sosial
Item aliran selalu dikaitkan dengan kontak mentah. Link android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID ke nilai _ID
untuk kontak mentah. Tipe akun dan nama akun kontak mentah juga disimpan dalam baris item aliran.
Simpanlah data dari aliran Anda dalam kolom-kolom berikut:
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- Diperlukan. Tipe akun pengguna untuk kontak mentah yang dikaitkan dengan item aliran ini. Ingatlah untuk mengatur nilai ini saat Anda menyisipkan item aliran.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- Diperlukan. Nama akun pengguna untuk kontak mentah yang dikaitkan dengan item aliran ini. Ingatlah untuk mengatur nilai ini saat Anda menyisipkan item aliran.
- Kolom identifier
-
Diperlukan. Anda harus memasukkan kolom identifier berikut saat menyisipkan item aliran:
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: Nilai android.provider.BaseColumns#_ID kontak yang dikaitkan dengan item aliran ini.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: Nilai android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY dari kontak yang dikaitkan dengan item aliran ini.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: Nilai android.provider.BaseColumns#_ID kontak mentah yang dikaitkan dengan item aliran ini.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- Opsional. Menyimpan informasi rangkuman yang bisa Anda tampilkan di awal item aliran.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
Teks item aliran, baik materi yang diposting oleh sumber item, maupun keterangan beberapa aksi yang menghasilkan item aliran. Kolom ini bisa berisi sembarang gambar resource pemformatan dan tersemat yang bisa dirender oleh
fromHtml()
. Penyedia bisa memotong atau menghapus materi yang panjang, tetapi penyedia akan mencoba menghindari memutus tag. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- String teks berisi waktu item aliran yang disisipkan atau diperbarui, berupa milidetik sejak waktu patokan. Aplikasi yang menyisipkan atau memperbarui item aliran bertanggung jawab memelihara kolom ini; aplikasi tidak dipelihara secara otomatis oleh Penyedia Kontak.
Untuk menampilkan informasi yang mengidentifikasi item stream Anda, gunakan android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL, dan android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE untuk menautkan resource ke dalam aplikasi Anda.
Tabel android.provider.ContactsContract.StreamItems juga berisi kolom android.provider.ContactsContract.StreamItemsColumns#SYNC1 sampai android.provider.ContactsContract.StreamItemsColumns#SYNC4 untuk penggunaan eksklusif adaptor sinkronisasi.
Foto aliran sosial
Tabel android.provider.ContactsContract.StreamItemPhotos menyimpan foto-foto yang dikaitkan dengan item aliran. Kolom android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID tabel ini menautkan ke nilai dalam kolom _ID
dari tabel android.provider.ContactsContract.StreamItems. Acuan foto disimpan dalam tabel pada kolom-kolom berikut:
- Kolom android.provider.ContactsContract.StreamItemPhotos#PHOTO (BLOB).
- Representasi biner foto, yang diubah ukurannya oleh penyedia untuk storage dan tampilan. Kolom ini tersedia untuk kompatibilitas mundur dengan versi Penyedia Kontak sebelumnya yang menggunakannya untuk menyimpan foto. Akan tetapi, pada versi saat ini Anda tidak boleh menggunakan kolom ini untuk menyimpan foto. Sebagai gantinya, gunakan android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID atau android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (keduanya dijelaskan dalam poin-poin berikut) untuk menyimpan foto dalam sebuah file. Kolom ini sekarang berisi thumbnail foto, yang tersedia untuk dibaca.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Identifier numerik foto untuk kontak mentah. Tambahkan nilai ini ke konstanta
DisplayPhoto.CONTENT_URI
untuk mendapatkan URI materi yang menunjuk ke satu file foto, kemudian panggilopenAssetFileDescriptor()
untuk mendapatkan handle ke file foto. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
URI materi menunjuk langsung ke file foto untuk foto yang diwakili oleh baris ini. Panggillah
openAssetFileDescriptor()
dengan URI ini untuk mendapatkan handle ke file foto.
Menggunakan tabel aliran sosial
Tabel-tabel ini sama fungsinya dengan tabel-tabel utama lainnya dalam Penyedia Kontak, kecuali:
- Tabel-tabel ini memerlukan izin akses tambahan. Untuk membaca dari tabel, aplikasi Anda harus memiliki izin android.Manifest.permission#READ_SOCIAL_STREAM. Untuk memodifikasi tabel, aplikasi Anda harus memiliki izin android.Manifest.permission#WRITE_SOCIAL_STREAM.
-
Untuk tabel android.provider.ContactsContract.StreamItems, jumlah baris yang disimpan bagi setiap kontak mentah adalah terbatas. Setelah batasnya tercapai, Penyedia Kontak akan ruang untuk mengosongkan baris item aliran baru dengan menghapus secara otomatis baris yang memiliki android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP yang lebih lama. Untuk mendapatkan batas, keluarkan kueri ke URI materi android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Anda bisa membiarkan semua argumen selain URI materi yang disetel ke
null
. Kueri menampilkan sebuah Kursor yang berisi baris tunggal, dengan kolom tunggal android.provider.ContactsContract.StreamItems#MAX_ITEMS.
Class android.provider.ContactsContract.StreamItems.StreamItemPhotos menetapkan sub-tabel android.provider.ContactsContract.StreamItemPhotos yang berisi baris foto untuk satu item aliran.
Interaksi aliran sosial
Data aliran sosial yang dikelola oleh Penyedia Kontak, bersama aplikasi kontak perangkat, menawarkan cara andal untuk menghubungkan sistem jaringan sosial Anda dengan kontak yang ada. Tersedia fitur-fitur berikut:
- Dengan menyinkronkan layanan jaringan sosial ke Penyedia Kontak dengan adaptor sinkronisasi, Anda bisa mengambil aktivitas terbaru untuk kontak pengguna dan menyimpannya dalam tabel-tabel android.provider.ContactsContract.StreamItems dan android.provider.ContactsContract.StreamItemPhotos untuk digunakan nanti.
- Selain sinkronisasi rutin, Anda bisa memicu adaptor sinkronisasi agar mengambil data tambahan bila pengguna memilih sebuah kontak untuk ditampilkan. Hal ini memungkinkan adaptor sinkronisasi Anda mengambil foto resolusi tinggi dan item aliran terbaru untuk kontak.
- Dengan mendaftarkan notifikasi pada aplikasi kontak perangkat dan Penyedia Kontak, Anda bisa menerima intent saat kontak ditampilkan, dan pada saat itu memperbarui status kontak dari layanan Anda. Pendekatan ini mungkin lebih cepat dan menggunakan bandwidth lebih sedikit daripada melakukan sinkronisasi penuh dengan adaptor sinkronisasi.
- Pengguna bisa menambahkan kontak ke layanan jaringan sosial Anda sambil melihat kontak dalam aplikasi kontak perangkat. Anda mengaktifkannya dengan fitur "undang kontak", yang Anda aktifkan dengan kombinasi aktivitas yang menambahkan kontak yang ada ke jaringan Anda, dan file XML yang menyediakan aplikasi kontak perangkat dan Penyedia Kontak dengan detail aplikasi Anda.
Sinkronisasi rutin item aliran dengan Penyedia Kontak sama dengan sinkronisasi lainnya. Untuk mengetahui selengkapnya tentang sinkronisasi, lihat bagian Adaptor Sinkronisasi Penyedia Kontak. Mendaftarkan notifikasi dan mengundang kontak dibahas dalam dua bagian berikutnya.
Pendaftaran untuk menangani tampilan jaringan sosial
Untuk mendaftarkan adaptor sinkronisasi agar menerima notifikasi saat pengguna menampilkan kontak yang dikelola oleh adaptor sinkronisasi Anda:
-
Buat file yang bernama
contacts.xml
dalam direktorires/xml/
project Anda. Jika sudah memiliki file ini, langkah ini boleh dilewati. -
Dalam file ini, tambahkan elemen
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Jika elemen ini sudah ada, langkah ini boleh dilewati. -
Untuk mendaftarkan layanan yang diberitahukan saat pengguna membuka halaman detail kontak dalam aplikasi kontak perangkat, tambahkan atribut
viewContactNotifyService="serviceclass"
ke elemen, denganserviceclass
sebagai nama class mutlak (fully qualified) dari layanan yang seharusnya menerima intent dari aplikasi kontak perangkat. Untuk layanan notifier, gunakan class yang memperluasIntentService
, guna memudahkan layanan untuk menerima intent. Data dalam intent yang masuk berisi URI materi dari kontak mentah yang diklik pengguna. Untuk layanan notifier, Anda bisa mengikatnya ke kemudian memanggil adaptor sinkronisasi Anda untuk memperbarui data bagi kontak mentah.
Untuk mendaftarkan aktivitas agar dipanggil saat pengguna mengeklik item aliran atau foto atau keduanya:
-
Buat file yang bernama
contacts.xml
dalam direktorires/xml/
project Anda. Jika sudah memiliki file ini, langkah ini boleh dilewati. -
Dalam file ini, tambahkan elemen
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Jika elemen ini sudah ada, langkah ini boleh dilewati. -
Untuk mendaftarkan salah satu aktivitas Anda guna menangani klik oleh pengguna pada item aliran dalam aplikasi kontak perangkat, tambahkan atribut
viewStreamItemActivity="activityclass"
ke elemen, denganactivityclass
sebagai nama class mutlak (fully-qualified) dari aktivitas yang harus menerima intent dari aplikasi kontak perangkat. -
Untuk mendaftarkan salah satu aktivitas Anda guna menangani klik oleh pengguna pada foto aliran dalam aplikasi kontak perangkat, tambahkan atribut
viewStreamItemPhotoActivity="activityclass"
ke elemen, denganactivityclass
adalah class nama mutlak aktivitas yang harus menerima intent dari aplikasi kontak perangkat.
Elemen <ContactsAccountType>
dijelaskan lebih detail di bagian Elemen <ContactsAccountType>.
Intent yang masuk berisi URI materi dari materi atau foto yang diklik pengguna. Untuk mendapatkan aktivitas terpisah bagi item teks dan foto, gunakan kedua atribut dalam file yang sama.
Berinteraksi dengan layanan jaringan sosial Anda
Pengguna tidak harus meninggalkan aplikasi perangkat kontak untuk mengundang kontak ke situs jaringan sosial Anda. Sebagai gantinya, Anda bisa meminta aplikasi kontak perangkat mengirimkan intent untuk mengundang kontak ke salah satu aktivitas Anda. Untuk mempersiapkannya:
-
Buat file yang bernama
contacts.xml
dalam direktorires/xml/
project Anda. Jika sudah memiliki file ini, langkah ini boleh dilewati. -
Dalam file ini, tambahkan elemen
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Jika elemen ini sudah ada, langkah ini boleh dilewati. -
Tambahkan atribut-atribut berikut:
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
adalah nama class mutlak aktivitas yang harus menerima intent ini. Nilaiinvite_action_label
adalah string teks yang ditampilkan dalam menu Tambahkan Koneksi dalam aplikasi kontak perangkat.
Catatan: ContactsSource
adalah nama tag yang sudah tidak digunakan lagi untuk ContactsAccountType
.
Referensi contacts.xml
File contacts.xml
berisi elemen XML yang mengontrol interaksi adaptor sinkronisasi Anda dan aplikasi dengan aplikasi kontak dan Penyedia Kontak. Elemen-elemen ini dijelaskan di bagian-bagian selanjutnya.
Elemen <ContactsAccountType>
Elemen <ContactsAccountType>
mengontrol interaksi aplikasi Anda dengan aplikasi kontak. Sintaksnya adalah sebagai berikut:
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android" inviteContactActivity="activity_name" inviteContactActionLabel="invite_command_text" viewContactNotifyService="view_notify_service" viewGroupActivity="group_view_activity" viewGroupActionLabel="group_action_text" viewStreamItemActivity="viewstream_activity_name" viewStreamItemPhotoActivity="viewphotostream_activity_name">
dimuat dalam:
res/xml/contacts.xml
bisa berisi:
<ContactsDataKind>
Keterangan:
Mendeklarasikan komponen Android dan label UI yang memungkinkan pengguna mengundang salah satu kontak ke jaringan sosial, memberi tahu pengguna bila salah satu aliran jaringan sosial diupdate, dan seterusnya.
Perhatikan bahwa awalan atribut android:
tidak perlu untuk atribut-atribut <ContactsAccountType>
.
Atribut:
inviteContactActivity
- Nama class mutlak aktivitas dalam aplikasi yang Anda ingin aktifkan bila pengguna memilih Tambahkan koneksi dari aplikasi kontak perangkat.
inviteContactActionLabel
-
String teks yang ditampilkan untuk aktivitas yang ditetapkan dalam
inviteContactActivity
, dalam menu Tambahkan koneksi. Misalnya, Anda bisa menggunakan string "Ikuti di jaringan saya". Anda bisa menggunakan resource identifier string untuk tabel ini. viewContactNotifyService
- Nama class mutlak layanan dalam aplikasi Anda yang harus menerima notifikasi saat pengguna menampilkan kontak. Notifikasi ini dikirimkan oleh aplikasi kontak perangkat; hal ini memungkinkan aplikasi Anda menunda operasi yang banyak memproses data hingga dibutuhkan. Misalnya, aplikasi Anda bisa merespons notifikasi ini dengan membaca dalam dan menampilkan foto resolusi tinggi kontak dan item aliran sosial terbaru. Fitur ini dijelaskan lebih detail di bagian Interaksi aliran sosial.
viewGroupActivity
- Nama class mutlak aktivitas dalam aplikasi yang bisa menampilkan informasi grup. Bila pengguna mengklik label grup dalam aplikasi kontak perangkat, UI aktivitas ini akan ditampilkan.
viewGroupActionLabel
-
Label yang ditampilkan aplikasi kontak untuk kontrol UI yang memungkinkan pengguna melihat grup dalam aplikasi Anda.
Identifier resource string diperbolehkan untuk atribut ini.
viewStreamItemActivity
- Nama class mutlak aktivitas dalam aplikasi Anda yang diluncurkan aplikasi kontak perangkat bila pengguna mengklik item aliran untuk kontak mentah.
viewStreamItemPhotoActivity
- Nama class mutlak aktivitas yang diluncurkan aplikasi kontak perangkat bila pengguna mengklik foto dalam item aliran untuk kontak mentah.
Elemen <ContactsDataKind>
Elemen <ContactsDataKind>
mengontrol tampilan baris data khusus aplikasi Anda dalam UI aplikasi kontak. Sintaksnya adalah sebagai berikut:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
dimuat dalam:
<ContactsAccountType>
Keterangan:
Gunakan elemen ini untuk memerintahkan aplikasi kontak agar menampilkan materi baris data khusus sebagai bagian dari detail kontak mentah. Setiap elemen turunan <ContactsDataKind>
<ContactsAccountType>
mewakili tipe baris data khusus yang ditambahkan adaptor sinkronisasi Anda ke tabel ContactsContract.Data
. Tambahkan satu elemen <ContactsDataKind>
untuk setiap tipe MIME khusus yang Anda gunakan. Anda tidak harus menambahkan elemen jika Anda memiliki baris data khusus yang datanya tidak ingin ditampilkan.
Atribut:
android:mimeType
-
Tipe MIME khusus yang telah Anda definisikan untuk salah satu tipe baris data khusus dalam
ContactsContract.Data
. Misalnya, nilaivnd.android.cursor.item/vnd.example.locationstatus
bisa berupa tipe MIME khusus untuk baris data yang mencatat lokasi kontak yang terakhir diketahui. android:icon
- Resource dapat digambar Android yang ditampilkan aplikasi kontak di samping data Anda. Gunakan ini untuk menunjukkan kepada pengguna bahwa data berasal dari layanan Anda.
android:summaryColumn
- Nama kolom untuk yang pertama dari dua nilai yang diambil dari baris data. Nilai ditampilkan sebagai baris pertama entri untuk baris data ini. Baris pertama dimaksudkan untuk digunakan sebagai rangkuman data, tetapi itu bersifat opsional. Lihat juga android:detailColumn.
android:detailColumn
-
Nama kolom untuk yang kedua dari dua nilai yang diambil dari baris data. Nilai ditampilkan sebagai baris kedua entri untuk baris data ini. Lihat juga
android:summaryColumn
.
Fitur tambahan Penyedia Kontak
Di samping fitur-fitur utama yang dijelaskan di bagian sebelumnya, Penyedia Kontak menawarkan fitur-fitur berguna ini untuk digunakan bersama data kontak:
- Grup kontak
- Fitur foto
Grup kontak
Penyedia Kontak secara opsional bisa melabeli kumpulan kontak terkait dengan data grup. Jika server yang dikaitkan dengan akun pengguna ingin mempertahankan grup, adaptor sinkronisasi untuk tipe akun dari akun itu harus mentransfer data grup antara Penyedia Kontak dan server. Bila pengguna menambahkan kontak baru ke server, kemudian memasukkan kontak ini dalam grup baru, adaptor sinkronisasi harus menambahkan grup baru ke tabel ContactsContract.Groups
. Grup atau grup-grup yang memiliki kontak disimpan dalam tabel ContactsContract.Data
, dengan menggunakan tipe MIME ContactsContract.CommonDataKinds.GroupMembership
.
Jika Anda mendesain adaptor sinkronisasi yang akan menambahkan data kontak mentah dari server ke Penyedia Kontak, dan Anda tidak menggunakan grup, maka Anda harus memberi tahu penyedia itu agar membuat data Anda terlihat. Dalam kode yang dijalankan bila pengguna menambahkan akun ke perangkat, perbarui baris ContactsContract.Settings
yang ditambahkan Penyedia Kontak untuk akun. Dalam baris ini, setel nilai kolom Settings.UNGROUPED_VISIBLE
ke 1. Bila melakukannya, Penyedia Kontak akan selalu membuat data kontak Anda terlihat, meskipun Anda tidak menggunakan grup.
Foto kontak
Tabel ContactsContract.Data
menyimpan foto sebagai baris dengan tipe MIME Photo.CONTENT_ITEM_TYPE
. Kolom CONTACT_ID
baris yang ditautkan ke kolom _ID
kontak mentah yang memiliki kolom itu. Class ContactsContract.Contacts.Photo
menetapkan subtabel ContactsContract.Contacts
yang berisi informasi foto untuk foto utama kontak, yang merupakan foto utama dari kontak mentah utama kontak itu. Demikian pula, class ContactsContract.RawContacts.DisplayPhoto
menetapkan subtabel ContactsContract.RawContacts
yang berisi informasi foto untuk foto utama kontak mentah.
Dokumentasi acuan untuk ContactsContract.Contacts.Photo
dan ContactsContract.RawContacts.DisplayPhoto
berisi contoh-contoh pengambilan informasi foto. Tidak ada class praktis untuk mengambil thumbnail utama kontak mentah, tetapi Anda bisa mengirim kueri ke tabel ContactsContract.Data
, dengan memilih kontak mentah _ID
, Photo.CONTENT_ITEM_TYPE
, dan kolom IS_PRIMARY
untuk menemukan baris foto utama kontak mentah.
Data aliran sosial untuk seseorang bisa juga disertai foto. Data ini disimpan dalam tabel android.provider.ContactsContract.StreamItemPhotos, yang dijelaskan lebih detail di bagian Foto aliran sosial.