연락처의 사진을 어떻게 불러올 수 있나요?


안드로이드에서 연락처의 사진을 불러들이는데에 어려움을 겪고 있습니다. 방법을 구글링해봤지만, 관련된 자료를 찾지 못했네요. 연락처를 쿼리하여 사진을 불러들이는 예제를 갖고 계신분 없나요?

startActivityForResult(new Intent(Intent.ACTION_PICK,ContactsContract.CommonDataKinds.Phone.CONTENT_URI),PICK_CONTACT_REQUEST)

위 코드를 이용하여 액티비티 result로부터 불러온 contactUri는 아래와 같습니다.

content://com.android.contacts/data/1557

연락처를 불러오는 loadContact(..)함수는 문제없이 작동합니다만, 사진을 불러오는 getPhoto() 함수를 호출하면 사진 InputStream에 null 값이 들어오네요. 이게 또 헷갈리는게 URI 값이 다릅니다. contactPhotoUri는 아래와 같습니다.

content://com.android.contacts/contacts/1557

아래 코드의 주석을 참고하세요.

class ContactAccessor {

/**
 * 연락처 정보를 불러오는 함수
 */
public ContactInfo loadContact(ContentResolver contentResolver, Uri contactUri) {

    //contactUri --> content://com.android.contacts/data/1557

    ContactInfo contactInfo = new ContactInfo();

    // 특정인에 대한 이름을 불러옴
    Cursor cursor = contentResolver.query(contactUri,
                                        new String[]{Contacts._ID, 
                                                     Contacts.DISPLAY_NAME, 
                                                     Phone.NUMBER,
                                                     Contacts.PHOTO_ID}, null, null, null);
    try {
        if (cursor.moveToFirst()) {
            contactInfo.setId(cursor.getLong(0));
            contactInfo.setDisplayName(cursor.getString(1));
            contactInfo.setPhoneNumber(cursor.getString(2));
        }
    } finally {
        cursor.close();
    }        
    return contactInfo;  // <-- 연락처의 정보를 반환
}

public Bitmap getPhoto(ContentResolver contentResolver, Long contactId) {
    Uri contactPhotoUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);

    // contactPhotoUri --> content://com.android.contacts/contacts/1557

    InputStream photoDataStream = Contacts.openContactPhotoInputStream(contentResolver,contactPhotoUri); // <-- 이 부분이 항상 null을 반환합니다.
    Bitmap photo = BitmapFactory.decodeStream(photoDataStream);
    return photo;
}

public class ContactInfo {

    private long id;
    private String displayName;
    private String phoneNumber;
    private Uri photoUri;

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public String getDisplayName() {
        return displayName;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public Uri getPhotoUri() {
        return this.photoUri;
    }

    public void setPhotoUri(Uri photoUri) {
        this.photoUri = photoUri;
    }

    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }

}
}

분명히 여기 어딘가에서 실수를 한 것 같은데, 문제점을 찾을 수가 없네요. 감사합니다.

  • 2016년 06월 03일에 작성됨

조회수 148


1 답변


좋아요
0
싫어요
채택취소하기

썸네일을 띄우는 문제와 관련하여 많은 질문과 답변을 보아왔지만 이를 해결하는 방법을 두어개 정도밖에 볼 수 없었고 좋은 해결책을 제시한 경우를 보지 못했던 관계로 제 해결책을 올립니다.

아래 클래스는 Context, QuickContactBadge와 전화번호를 불러와 특정 전화번호에 대해 사용가능한 이미지가 있다면 이를 불러옵니다.

클래스는 아래와 같습니다.

public final class QuickContactHelper {

private static final String[] PHOTO_ID_PROJECTION = new String[] {
    ContactsContract.Contacts.PHOTO_ID
};

private static final String[] PHOTO_BITMAP_PROJECTION = new String[] {
    ContactsContract.CommonDataKinds.Photo.PHOTO
};

private final QuickContactBadge badge;

private final String phoneNumber;

private final ContentResolver contentResolver;

public QuickContactHelper(final Context context, final QuickContactBadge badge, final String phoneNumber) {

    this.badge = badge;
    this.phoneNumber = phoneNumber;
    contentResolver = context.getContentResolver();

}

public void addThumbnail() {

    final Integer thumbnailId = fetchThumbnailId();
    if (thumbnailId != null) {
        final Bitmap thumbnail = fetchThumbnail(thumbnailId);
        if (thumbnail != null) {
            badge.setImageBitmap(thumbnail);
        }
    }

}

private Integer fetchThumbnailId() {

    final Uri uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
    final Cursor cursor = contentResolver.query(uri, PHOTO_ID_PROJECTION, null, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");

    try {
        Integer thumbnailId = null;
        if (cursor.moveToFirst()) {
            thumbnailId = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_ID));
        }
        return thumbnailId;
    }
    finally {
        cursor.close();
    }

}

final Bitmap fetchThumbnail(final int thumbnailId) {

    final Uri uri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, thumbnailId);
    final Cursor cursor = contentResolver.query(uri, PHOTO_BITMAP_PROJECTION, null, null, null);

    try {
        Bitmap thumbnail = null;
        if (cursor.moveToFirst()) {
            final byte[] thumbnailBytes = cursor.getBlob(0);
            if (thumbnailBytes != null) {
                thumbnail = BitmapFactory.decodeByteArray(thumbnailBytes, 0, thumbnailBytes.length);
            }
        }
        return thumbnail;
    }
    finally {
        cursor.close();
    }

}

}

그리고 아래는 액티비티에서 이를 사용하는 방법입니다 :

String phoneNumber = "...";
QuickContactBadge badge = (QuickContactBadge) view.findViewById(R.id.friend);
new QuickContactHelper(this, badge, phoneNumber).addThumbnail();

프라그먼트에서 사용한다면 약간 다릅니다 :

String phoneNumber = "...";
QuickContactBadge badge = (QuickContactBadge) view.findViewById(R.id.friend);
new QuickContactHelper(getActivity(), badge, phoneNumber).addThumbnail();

물론 이 코드를 보다 효율적으로 사용하기 위해선 코드를 다소 수정할 필요가 있습니다. 예를 들어 만약에 질문하신 분께서 메시지 타임라인을 구성하고자 한다면 그때그때 새로 사진 파일을 불러들이는 것보다는 같은 사진 파일을 동일한 전화번호에 해당하는 모든 배지에 재사용하고 싶을 겁니다. 하지만 이 답변의 목적은 보다 명확히 요점을 보이기 위해, 사용하는 분들이 독창적으로 완성시켜 사용할 수 있는 해결책을 제공하는 것입니다. 이 해결책은 Android 4.0 환경에서 빌드되어 테스트되었으며, 4.1에서 또한 테스트되었습니다.

  • 2016년 06월 04일에 작성됨

로그인이 필요한 기능입니다.

Hashcode는 개발자들을 위한 무료 QnA사이트 입니다. 작성한 답변에 다른 개발자들이 댓글을 작성하거나 좋아요/싫어요를 할 수 있기 때문에 계정을 필요로 합니다.
► 로그인
► 계정만들기
Close