zackey推し

プログラミングとか色々書いてきます

【Xamarin】アドレス帳にアクセスする(Android側)

はじめに

先日、JXUG Xamarin.iOS & Xamarin.Androidハンズオン! - connpassへ参加させていただき、 JavaC#(Xamarin.Android)へ変換する際のコツを学ばさせていただきました。 (後ほど参加レポート書きたいと思います。)

またプライベートで、

  • Android端末からアドレス帳の情報をバックアップしたい
  • バックアップはクラウドではなくローカル(端末内)に保存したい

という事を行いたい方がいて、目ぼしいアプリが見つからないとのことを聞き、 これはXamarin.Androidアプリを作ってみるのにちょうどいいのでは? ( ̄ー ̄)ニヤリ と思い、作成しました。

github.com

実装概要は以下です。

  • Android,iOSアプリとして動くよう実装
  • アドレス帳から「名前、読み仮名、電話番号、メールアドレス」の一覧を取得
  • jsonファイルとして保存する → あとで手動で抜き出します・・・

プライベート的なあれで申し訳ないのですが、iOS側のみ動作未確認です。。。

※近々、SimなしiPhone買って、動作確認しようかと思います。  Androidは動作確認済みです。

Xamarinとは?

ざっくり説明すると、Android,iOS,WindowsアプリをC#で 実装できるクロスプラットフォーム開発環境のことです。

詳しくは、エクセルソフト田淵さんがまとめてくださった、 こちらの記事をご参照ください。

ytabuchi.hatenablog.com

Visual Studio 2017でのXamarinインストール手順も記述してくださっていますので、 新規に始める方におすすめです。

解説

今回解説するのは、Xamarin.Androidでのアドレス帳へのアクセス方法です。 (Xamarin.iOSは動作確認後に投稿します。)

すでに同じような解説記事を書いてくださっている方がいらっしゃるのですが、

アドレス帳から名前と生年月日とメールアドレスを取得する方法 | Xamarin.Forms - ITブログ時々なんでもブログ

今回は、Android,iOS固有機能を実装する際のノウハウを積みたかったため、 基本的に自分で調べて実装を進めてみました。 実装するまでに踏んだ手順を記載しますので、参考にしていただければと思います。

Xamarin.Android

Xamarin.Androidは連絡先プロバイダという機能を利用してアクセスします。

連絡先プロバイダは、Xamarinの公式ページでも軽く(英文で)説明してくれていますが、 きちんと理解されたい方(&自分と同じく英語に弱い同士)は、 Android Developerに記述された連絡先プロバイダについての説明文(日本語)を読みましょう。

developer.android.com

連絡先プロバイダをざっくり理解感じ、SQLって感じですかね。。。

Androidのアドレス帳に登録する電話番号やメールアドレスって、 一人あたり複数個登録できますよね? なので、そういった項目を全て抽出したい場合は、 各(電話番号&メールアドレス)テーブルみたいのを参照してねー、 って感じで理解しました。

そんで、JavaのコードをXamarin.Android(C#)で記述したのが、 以下のコードになります。

ContactService.cs

/// <summary>
/// アドレス帳取得
/// </summary>
/// <returns></returns>
public ObservableCollection<Contact> GetContactList()
{
    ObservableCollection<Contact> list = new ObservableCollection<Contact>();

    // アドレス帳から取得する名前を指定
    string[] projection = 
    {
        ContactsContract.Contacts.InterfaceConsts.Id,   // ID(おそらくアドレス帳一意となる値)
        ContactsContract.Contacts.InterfaceConsts.DisplayName,  // 名前
        ContactsContract.Contacts.InterfaceConsts.PhoneticName, // 読みがな
    };

    // アクティビティ取得
    //  Forms.Context から取得すると警告が発生するため、以下で取得するほうが良さそう
    var activity = Android.App.Application.Context;

    // アクティビティが取得できた場合にクエリ発行
    if(activity != null)
    {
        var cursor = activity.ContentResolver.Query(
            ContactsContract.Contacts.ContentUri,
            //ContactsContract.Data.ContentUri,
            projection,
            string.Empty,
            null,
            ContactsContract.Contacts.InterfaceConsts.DisplayName
        );

        if (0 < cursor.Count)
        {
            while (cursor.MoveToNext())
            {
                Contact model = new Contact();
                string id = cursor.GetString(cursor.GetColumnIndex(projection[0]));

                model.Name = cursor.GetString(cursor.GetColumnIndex(projection[1]));
                model.Kana = cursor.GetString(cursor.GetColumnIndex(projection[2]));
                model.Tel = GetTel(id, activity.ContentResolver);
                model.Email = GetEmail(id, activity.ContentResolver);

                list.Add(model);
            }
        }
    }

    return list;
}

/// <summary>
/// 電話番号取得
/// </summary>
/// <param name="id">ContactId</param>
/// <param name="cr">Android.App.Application.Context.ContentResolver</param>
/// <returns>'/'区切りの電話番号文字列</returns>
private ObservableCollection<string> GetTel(string id, ContentResolver cr)
{
    ObservableCollection<string> result = new ObservableCollection<string>();

    var cursor = cr.Query(
        ContactsContract.CommonDataKinds.Phone.ContentUri,
        null,
        ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId + "=" + id,
        null,
        null
    );

    if(0 < cursor.Count)
    {
        while(cursor.MoveToNext())
        {
            string val = string.Empty;
            val = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Phone.Number));
            if(!string.IsNullOrEmpty(val))
            {
                result.Add(val);
            }
        }
    }

    return result;
}

/// <summary>
/// メールアドレス取得
/// </summary>
/// <param name="id">ContactId</param>
/// <param name="cr">Android.App.Application.Context.ContentResolver</param>
/// <returns>'/'区切りのメールアドレス文字列</returns>
private ObservableCollection<string> GetEmail(string id, ContentResolver cr)
{
    ObservableCollection<string> result = new ObservableCollection<string>();

    var cursor = cr.Query(
        ContactsContract.CommonDataKinds.Email.ContentUri,
        null,
        ContactsContract.CommonDataKinds.Email.InterfaceConsts.ContactId + "=" + id,
        null,
        null
    );

    if (0 < cursor.Count)
    {
        while (cursor.MoveToNext())
        {
            string val = string.Empty;
            val = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Email.Address));
            if (!string.IsNullOrEmpty(val))
            {
                result.Add(val);
            }
        }
    }

    return result;
}

マニフェストファイルへパーミッションの追加もお忘れなく。

AndroidManifest.xml

<uses-permission android:name="android.permission.READ_CONTACTS" />

Xamarin.Android側を実装した感想

JavaC#への変換はそんなに大変ではないなーって感じでした。 連絡先プロバイダについて、Android Developerの説明も丁寧でしたし、 参考文献がない場合は、Xamarin.Androidは以下のように実装を進めてく方向がよさそうだな、 と思いました。

  1. 実現させたい機能の実装方法をAndroid Developerから学ぶ
  2. Xamarin.Androidの公式ページにて実装方法を検索
  3. 2がダメだったら、Javaで実装された内容に相当するC#のクラスやメソッドを探す