LoginSignup
2
2
Android強化月間 - Androidアプリ開発の知見を共有しよう -

Androidの連絡先からvCardを作成する

Last updated at Posted at 2023-09-30

概要

ContactsContract.Contacts.CONTENT_VCARD_URIを使って連絡先をvCard形式で取得します。

詳細

Androidには、連絡先からvCard形式のデータを取得するContent URIがあるので、それを利用して連絡先をvCardにして取得を行います。
利用するには READ_CONTACTS のパーミッションが必要になるので適宜取得してください。

vCardを取得したい連絡先を選択

まずはvCard形式で取得したい連絡先を選択します。おそらく最も手軽であろう連絡先読み込みIntentで連絡先をリストから選択して、その結果を受け取るようにします。

        val pickContact = registerForActivityResult(ActivityResultContracts.PickContact()) {

        }
        pickContact.launch()

このコードで、連絡先アクティビティが開き、連絡先を指定したらregisterForActivityResultのコールバックにURIが戻ってきます。

連絡先からLOOKUP_KEYを取得

次に、連絡先から、vCardの取得に必要なLOOKUP_KEYを取得します。
取得したURI(it)でLOOKUP_KEY取得用のCursorを作り、Cursorから目的の連絡先のLOOKUP_KEYを取得します。

        val pickContact = registerForActivityResult(ActivityResultContracts.PickContact()) {
            val projection = arrayOf(
                ContactsContract.Contacts.LOOKUP_KEY,
            )

            val cursorForLookupKey = contentResolver.query(
                it!!,
                projection,
                null,
                null,
                null
            )
            var lookupKey: String = ""
            cursorForLookupKey?.apply {
                val index: Int = getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)
                while (moveToNext()) {
                    lookupKey = getString(index)
                }
            }

            cursorForLookupKey?.close()
        }
        pickContact.launch()

vCard URIからvCardを読み込み

そして、取得したLOOKUP_KEYと合わせてvCard取得用のURIを作成します。
この作成には Uri#withAppendedPath(Uri, String) を使います。

vCard用のURIが作成できたら、ContentResolver#openAssetFileDescriptor(Uri, String)でassetFileDescriptorを取得し、FileInputStreamをassetFileDescriptorで作成して読み込みます。

assetFileDescriptorのlengthは UNKNOWN_LENGTH になっている場合があるので、FileInputStreamを1バイトずつ読み込みます。

以下、vCard取得する処理の全体像になります。

        val pickContact = registerForActivityResult(ActivityResultContracts.PickContact()) {
            val projection = arrayOf(
                ContactsContract.Contacts.LOOKUP_KEY,
            )

            val cursorForLookupKey = contentResolver.query(
                it!!,
                projection,
                null,
                null,
                null
            )
            var lookupKey: String = ""
            cursorForLookupKey?.apply {
                val index: Int = getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)
                while (moveToNext()) {
                    lookupKey = getString(index)
                }
            }

            cursorForLookupKey?.close()

            // 追加
            val vcardUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI,
                lookupKey)
            val assetFileDescriptor = contentResolver.openAssetFileDescriptor(vcardUri,"r")
            val fileDescriptor = assetFileDescriptor?.fileDescriptor
            val input = FileInputStream(fileDescriptor)

            var bytes = byteArrayOf()
            var nextByte = 0
            while (true) {
                nextByte = input.read()
                if (nextByte == -1) {
                    break
                }
                bytes += nextByte.toByte()
            }
            val vCard = String(bytes)
            Log.d(TAG,"vCard ${vCard}")
            assetFileDescriptor?.close()
        }
        pickContact.launch()

vCardのデータについて

出力されたvCardはこんな感じになります。QUOTED-PRINTABLEでエンコードされている点に注意しましょう。

BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E7=B9=94=E7=94=B0;=E4=BF=A1=E9=95=B7;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E7=B9=94=E7=94=B0=E4=BF=A1=E9=95=B7
X-PHONETIC-FIRST-NAME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E3=81=AE=E3=81=B6=E3=81=AA=E3=81=8C
X-PHONETIC-LAST-NAME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E3=81=8A=E3=81=A0
TEL;WORK:090-222-xxxxx
EMAIL;WORK:nobu@example.com
ORG:
PHOTO;ENCODING=BASE64;JPEG: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


END:VCARD

vCard文字列に関する公式のAPIは存在しないと思うので、ex-vcardなどで確認するのが簡単だと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2