Xamarin.Android アドレス帳の列挙
1 コンテントプロバイダ
Androidのコンテントプロバイダは、アプリが保持するデータを他のアプリが使えるようにする機能です。アドレス帳の情報は、このコンテントプロパイダを利用して取得することができます。
(1) カーソルの取得コンテントプロパイダへのアクセスは、DBへのアクセスと同様、テーブルや列などを指定してカーソルを取得する事からはじまります。
カーソルを取得するための方法は、下記の3つです。
・ManagedQuery()
Androidの2.3(APIレベル10)用、Android3.0(APIレベル11)で推奨されていません。
・ContentResolver.Query()
カーソルは管理されておらず、コードで明示的にクローズしなければなりません。
・CursorLoader().LoadInBackground()
Android3.0(APIレベル11)で導入されたものです。V4互換ライブラリを使用すると古いバージョンでもアクセスできるそうです。
3つとも色々試して見たのですが、Android弱者な私は、ContentResolverが一番使いやすく感じました。ManagedQueryは、検索の件数が多いと、エミュレータでは突然死が発生し、CursorLoaderはスレッドからうまく利用できませんでした。(もっとAndroidのDBアクセスに慣れてから、改めて検証する必要性を感じています)
(2) パーミッション
アドレス帳へのアクセスには「READ_CONTACTS」(書き込む場合は、「WRITE_CONTACTS」)が必要です。パーミッションがないと、アクセス時に「java.lang.SecurityException」の例外が発生します。
(3) 列挙
ContentResolverを使用してアドレス帳からデータを列挙するコードは、下記のとおりです。
var uri = ContactsContract.Contacts.ContentUri; string[] projection ={ ContactsContract.Contacts.InterfaceConsts.Id, ContactsContract.Contacts.InterfaceConsts.DisplayName, }; var cursor = ContentResolver.Query( uri, // データの種類 projection, // 項目指定(nullを指定すると全項目) null, // フィルタ条件(nullを指定するとフィルタなし) null, // フィルタ用パラメータ null // ソート ); while (cursor.MoveToNext()){ //コンタクトID var id = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.Id)); //名前 var displayName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.DisplayName)); //電話番号 var phoneNumber = ""; var selectionStr = ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId + "=" + id; //フィルタ指定 var phones = ContentResolver.Query(ContactsContract.CommonDataKinds.Phone.ContentUri, null, selectionStr,null, null); while (phones.MoveToNext()){ var s = phones.GetString(phones.GetColumnIndex(ContactsContract.CommonDataKinds.Phone.Number)); phoneNumber += s + ","; } phones.Close(); //メールアドレス var email = ""; selectionStr = ContactsContract.CommonDataKinds.Email.InterfaceConsts.ContactId + "=" + id; //フィルタ指定 var emails = ContentResolver.Query(ContactsContract.CommonDataKinds.Email.ContentUri, null, selectionStr,null, null); while (emails.MoveToNext()){ var s = emails.GetString(emails.GetColumnIndex(ContactsContract.CommonDataKinds.Email.InterfaceConsts.Data)); email += s + ","; } emails.Close(); AppendLog(string.Format("{0} {1} {2} {3}\r\n", id, displayName, phoneNumber, email)); } cursor.Close();
JavaSDKのサンプルにある
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
や
ContactsContract.CommonDataKinds.Email.DATA1
が・・・
Xamarinでは、
ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId
ContactsContract.CommonDataKinds.Email.InterfaceConsts.Data
であり、微妙に名前空間の階層が深いので、ちょっと迷いました。
なお、close()を忘れるとtry-catchにもかからず、お亡くなりになるので、注意しないと嵌ります。
Xamarinの導入ページにあったContentResolverの記述は、ContentResolver.Query(.Queryが抜けている?)の間違いかと思います。
http://docs.xamarin.com/guides/android/platform_features/intro_to_content_providers/part_2_-_using_the_contacts_contentprovier/
2 エミュレータへの連絡帳インポート
エミュレータで試すと、アドレス帳からは当初1件も列挙できません。これは、当然といえば当然ですがエミュレータにデータが存在しないためです。テスト用データを作成してもいいのですが、面倒なので、実機からインポートする手順を記載しておきます。
※エミュレータにSDカードが無いと作業ができませんので、あらかじめ確保しておいてください。
(1)実機でSDカードへエクスポート
普段使っているandroid端末でsdカードへエクスポートしてvcfファイルを取得します。この作業は、アドレス帳のメニューから実行することができます。また、取得したファイルは「共有」からメール等でエミュレータの動作しているPCを送ります。(2)エミュレータへのデータコピー
取得したデータは、adb.exeを使用してエミュレータ内にcontacts.vcfという名前でコピーします。C:\Users\ユーザ名\AppData\Local\Android\android-sdk\platform-tools>adb push 00001.vcf /sdcard/contacts.vcf
/sdcardは、/mnt/sdcardへのシンボリックリンクなので、/mnt/sdcardの下にデータがコピーされているのが見れます。