SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

Xamarin.Android アドレス帳の列挙

【 Xamarin 記事一覧 】

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」の例外が発生します。

電話帳005

(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にもかからず、お亡くなりになるので、注意しないと嵌ります。

電話帳006

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

電話帳001
/sdcardは、/mnt/sdcardへのシンボリックリンクなので、/mnt/sdcardの下にデータがコピーされているのが見れます。

(3)エミュレータでのインポート

エミュレータのアドレス帳でSDカードからのインポートします。

電話帳002

電話帳003

電話帳004

【 Xamarin 記事一覧 】