2011年08月08日

1.6/2.0以上両対応のコンタクトリストへのアクセス

そんな訳で新たに作ったアプリを公開直前でIS01(OS1.6)で動作しないことに気づき、急遽延期した訳ですが、今回のアプリはコンタクトリストを使うので、OS2.0以降でアクセス方法が変わっていることが引っかかっていると思われました。
1.6のHT-03Aで動いていたのは不思議と言えば不思議なのかも知れませんが、なにせOS1.6のエミュレーターがまともに起動しないので、純粋な(?)1.6での確認が取れません。
いったんはバイナリを分けようと思ったのですが、AndroidマーケットのMultiple APK Supportではできるだけバイナリをひとつにしろとか書かれているようです。
仕方なく再度ググってみたところ、2chと以下のサイトにリフレクションを使わないとダメだという記載を見つけました。


Contactsのバージョン毎の吸収について(英文)
http://stackoverflow.com/questions/2044985/android-contactscontract-and-building-across-multiple-sdk-versions

そして、最終的に行き当たったのが、リファレンスのUsing the Contacts API。
http://developer.android.com/intl/ja/resources/articles/contacts.html

リフレクションというのは初めて耳にしたのですが、比較的分かりやすいと感じました「むいみこむ」様を参考とさせていただきました
http://muimi.com/j/reflection/


でも、おおよその概念はなんとなく分かりましたが、具体的な運用方法についてはもやもやした感じ。
とはいえ、具体的な運用とは今回の事例を指すので、もうリファレンスに書いてある通りにやってみようと決めました。
で、同じようにしたつもりなのが下記です。


<クラス ContactAccessor.java>

public abstract class ContactAccessor {
public abstract Uri getContactsUri();
public abstract Uri getPhotoIdUri(String contactId);
public abstract Uri getUri(String contactId);
public abstract Intent getContactPicker(String contacId);
}

<クラス ContactAccsOldApi.java>
public class ContactAccessorOldApi extends ContactAccessor{
final Uri CONTACTS_URI = android.provider.Contacts.People.CONTENT_URI;

@Override
public Uri getContactsUri() {
// OS1.60用コンタクトリリストのURIを返す
return CONTACTS_URI;
}

@Override
public Uri getPhotoIdUri(String contactId) {
// ID付画像URIを返す
Uri uri = ContentUris.withAppendedId(Uri.parse("content://contacts/photos"), Long.parseLong(contactId));
return uri;
}

@Override
public Uri getUri(String contactId) {
// コンタクトリストのID付URIを返す
     Uri uri = ContentUris.withAppendedId(CONTACTS_URI, Long.parseLong(contactId));
     return uri;
}

@Override
public Intent getContactPicker(String contactId) {
// コンタクトリスト表示用インテントを返す
     Uri uri = ContentUris.withAppendedId(CONTACTS_URI, Long.parseLong(contactId));
return new Intent(Intent.ACTION_VIEW,uri);
}
}

<クラス ContactAccsNewApi.java>
public class ContactAccessorNewApi extends ContactAccessor{
final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;

@Override
public Uri getContactsUri() {
// ContactsContractのURIを返す
return CONTACTS_URI;
}

@Override
public Uri getPhotoIdUri(String photoId) {
// ID付画像URIを返す
Uri uri = ContentUris.withAppendedId(Uri.parse("content://com.android.contacts/data"), Long.parseLong(photoId));
return uri;
}

@Override
public Uri getUri(String contactId) {
// コンタクトリストのID付URIを返す
Uri uri = ContentUris.withAppendedId(CONTACTS_URI, Long.parseLong(contactId));
return uri;
}

@Override
public Intent getContactPicker(String contactId) {
// コンタクトリスト表示用インテントを返す
Uri uri = ContentUris.withAppendedId(CONTACTS_URI, Long.parseLong(contactId));
return new Intent(Intent.ACTION_VIEW,uri);
}
}

<メソッド getContactImage() // 上記クラスを実際に呼び出す>
public BitmapDrawable getContactImage(String contactId){
// コンタクトリストの画像を取得する
BitmapDrawable bitmapDrawable = null;

if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR){
// OS2.0未満はphotoのURIでcontactIdを指定することで取得可能
Uri CONTACTS_URI = contactAccsOld.getPhotoIdUri(contactId); //コンタクトIDと合致するレコードを取得
String[] projPhoto = new String[]{"data"};
Cursor cursor = managedQuery(CONTACTS_URI,projPhoto,null,null,null);
cursor.moveToFirst();
if(cursor.moveToFirst()){
byte[] photoData = cursor.getBlob(0); // 画像をバイトで取得
if(photoData != null){
bitmapDrawable = new BitmapDrawable(BitmapFactory.decodeByteArray(photoData, 0, photoData.length)); //ビットマップからDrawableに変換して画像を設置
}
}
cursor.close();
}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR){
// OS2.0以上はphoto_idを取得してから、画像を取得する
Uri CONTACTS_URI = contactAccsNew.getUri(contactId);
Cursor cursor = managedQuery(CONTACTS_URI,null,"_ID = ? ",new String[]{contactId},null); // コンタクトIDと合致するレコードを取得
cursor.moveToFirst();
     if(cursor.moveToFirst()){
     String photoId = cursor.getString(cursor.getColumnIndexOrThrow("photo_id")); // photo_idを取得
if(photoId != null){
Uri uri = contactAccsNew.getPhotoIdUri(photoId); // photo_idのURIを取得
String[] projPhoto = new String[]{"data15"};
Cursor cur = managedQuery(uri,projPhoto,null,null,null);
cur.moveToFirst();
if(cur.moveToFirst()){
     byte[] photoData = cur.getBlob(0); // 画像をバイトで取得
     if(photoData != null){
     bitmapDrawable = new BitmapDrawable(BitmapFactory.decodeByteArray(photoData, 0, photoData.length)); //ビットマップからDrawableに変換して画像を設置
     }
}
cur.close();
}
     }
     cursor.close();
}
return bitmapDrawable;
}



(なお、コンタクトリストの画像取得については下記サイト様を参考にさせていただきました。
sak's Android Avenue様
http://sakplus.jp/2010/05/01/android2x-are_you_ready/)


これで動かしてみたところ、やはりIS01ではverifyErrorとなってしまいました。
もう一度2chを参照したところ、リフレクションだけでなくロードしないようにしなければ、とあったのでonCreate()でインスタンスの生成を分岐させてみました。

if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR){
contactAccsOld = new ContactAccessorOldApi();
}else{
contactAccsNew = new ContactAccessorNewApi();
}

そうしたところ、IS01でもなんとか動きました。
良かった。もっと高度なことを要求されたらどうしようかと思いました。(笑)
IS01ではContactsContract.Contacts.CONTENT_URIと記載されているだけでVerifyErrorになるようです。

ただ、今回リファレンス通りにやったのですが、抽象クラスとか確かに参考書では一通りやりましたけど、実際に使うとなると手探り状態でやっているような感覚でした。リファレンスだって英語なので、ソース全部書いてくんなきゃ分かんないよ!状態でとりあえず完成。
これでいいのか?リファレンスの最後にNow replace references to ContactsAccessorOldApi with references to ContactsAccessorとか書いてあるのは意味分かんないぞ!などと思いつつ、今のところこれで動作しているので良しとすることにしました。
ただし、IS01でeclipseからapkが転送された直後IS01でcom.android.LAUNCHERが強制終了してしまう問題が時々発生しているので、まだ何か処理が足りないのかも知れません。(でも、これは解決しないままリリースしてしまうかも。アンインストールしてやり直すとエラーがでない)

なんだか色々間違ったところがありそうですが、今のところ動作していればOKということを自分に言い聞かせているプラグラマーでした。(笑)
なお、上記のコードは素人が手探りで書いたものなので決して信用しないでください。

posted by 白虹 at 23:58| Comment(0) | TrackBack(0) | Android開発
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/47258788

この記事へのトラックバック