通过Android中的PHOTO_FILE_ID将联系人图像更改为大照片

10

这似乎适用于小图片:

ContentValues values = new ContentValues();

values.put(ContactsContract.Data.RAW_CONTACT_ID, id);
values.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, photo);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
if (photoRow >= 0) {
    context.getContentResolver().update(ContactsContract.Data.CONTENT_URI, values, ContactsContract.Data._ID + " = " + photoRow, null);
} else {
    context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
}

文档中我了解到,对于大图片,我需要设置PHOTO_FILE_ID,这样我就可以替换:
ContactsContract.CommonDataKinds.Photo.PHOTO

使用:

ContactsContract.CommonDataKinds.Photo.PHOTO_FILE_ID

然而,我需要提供PHOTO_FILE_ID而不是原始数据。我的问题是:

  1. 我如何保存照片(byte [])并获取PHOTO_FILE_ID?
  2. 如果已经有一张照片可用(PHOTO而非PHOTO_FILE_ID),我需要删除它以便看到大图像吗?或者如果大图像不覆盖它,该怎么删除它?
4个回答

20

你的答案是可行的,但不够高效,因为照片需要被编码成SQL查询并通过Android IPC进行传输。这也使得它受到Android IPC大小限制(即如果您的照片过大,则内容提供程序操作将失败)。

设置(创建或覆盖)RawContact(主要)照片最有效的方法是使用openAssetFileDescriptorContactsContract.RawContacts.DisplayPhoto URI,示例如下(示例来自Android文档):

public void writeDisplayPhoto(long rawContactId, byte[] photo) {
    Uri rawContactPhotoUri = Uri.withAppendedPath(
            ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
            RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
    try {
        AssetFileDescriptor fd =
            getContentResolver().openAssetFileDescriptor(rawContactPhotoUri, "rw");
        OutputStream os = fd.createOutputStream();
        os.write(photo);
        os.close();
        fd.close();
    } catch (IOException e) {
        // Handle error cases.
    }
}
这种方法唯一的缺点是它总是创建/替换RawContact的主要照片。如果RawContact没有照片,则会添加一个照片。不幸的是,无法使用openAssetFileDescriptorPHOTO_FILE_ID一起使用,因此不能使用此方法覆盖由其ID标识的特定照片。但是,在实际生活中,大多数联系人可能最多只有一张照片,所以这不是一个真正的限制。这将自动更新Photo.PHOTO列,并分配一个PHOTO_FILE_ID

@androiddeveloper 没有“永远都能行的最佳方法”。这取决于您的用例。如果您要更新现有联系人的图像,则最好使用此方法(PHOTO_FILE_ID + openAssetFileDescriptor)。有时,将图像发送到内容提供程序操作批处理中更可取。例如,它允许您在单个事务中创建包含图像的联系人。我们在插入新联系人时使用此方法,但我们还会预先缩放图像,以确保它们不会超出IPC限制。 - Marten
我的意思是,如果您查询了一个联系人(现有的),并获得了其联系人ID和查找键。现在您希望添加/更新其中的一张照片。这种方法总是有效的吗(假设您获得了原始联系人ID)?您写道,如果它有多个照片,这将不起作用。为什么?只是我在这里问了一下:https://dev59.com/z6Tja4cB1Zd3GeqPFKnw,并且在代码中得到了不同的答案,想知道是否有更好的方法。 - android developer
1
未同步的原始联系人(也称为本地联系人)具有帐户类型“null”。我已经阅读了您的问题,但它并没有真正说明您的应用程序正在做什么。实际上实现自己的帐户类型可能会更好。创建本地联系人应该只是最后的选择。 - Marten
我明白了。您能否向我展示如何创建一个新的RawContact数据,仅用于本地存储中的照片,并提供contactId和lookupKey? - android developer
我们开发了一个名为ContentPal的库,它使得处理这些操作更加容易。我们的存储库中有一些未决的拉取请求,应该提供了你所需要的几乎所有内容。你可以打开一个问题来获得帮助。我无法在评论中描述这个。此外,请注意,每个原始联系人必须至少有一个名称。 - Marten
显示剩余4条评论

4

最终我用以下方法解决了这个问题:

public void changeContactImage(String contactId, byte[] b) {
 ArrayList < ContentProviderOperation > ops = new ArrayList < > ();

 ops.add(ContentProviderOperation
  .newUpdate(
   ContactsContract.Data.CONTENT_URI)
  .withSelection(
   ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?",
   new String[] {
    contactId,
    ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
   })
  .withValue(ContactsContract.CommonDataKinds.Photo.DATA15, b).build());


 // Do something with the phone number...
 try {

  context.getContentResolver().
  applyBatch(ContactsContract.AUTHORITY, ops);

 } catch (RemoteException e) {
  Log.d("RemoteException", e.toString());
 } catch (OperationApplicationException e) {
  Log.d("OperationException", e.toString());
 }

}

3
PHOTO_FILE_ID 是您原始照片数据所在数据库中行的ID,而不是文件的ID(令人困惑)。根据我查阅的文档,实际上您可以避免使用它。在PHOTO_FILE_ID下方,如果存在,则将用于填充PHOTO_URI。在PHOTO_ID下方(如果PHOTO_FILE_ID存在,则保证填充),引用保存照片的数据表中的行。照片可以通过ID(此字段)或URI(参见PHOTO_THUMBNAIL_URI和PHOTO_URI)来引用。这意味着,如果您只使用PHOTO_URI,则会得到与使用openDisplayPhoto方法相同的结果图像。它还表明,URI方法更兼容第三方目录,因此可能更适合使用。

实际上,PHOTO_FILE_ID 是文件的 ID(更准确地说是文件名)。它是照片的文件名,存储在 /data/data/com.android.providers.contacts/files/photos 下。不幸的是,您无法使用此 ID 写入照片,只能读取它们。 - Marten
也许这不太清楚。我的意思是,您可以使用PHOTO_URI,通过它您可以打开一个输出流来保存新数据,而不必直接使用ID。文档中说PHOTO_FILE_ID是一个整数,因此不能作为文件名。 - Nick Cardoso
不能写入PHOTO_URI。这只用于读取(聚合)联系人的主要照片,不适用于RawContacts。尝试写入此URI将失败(请参见ContactsProvider2.java,第8033行)。关于文件名,你是对的,实际文件名是PHOTO_FILE_ID的十进制字符串表示形式。但最终这并不重要,因为它只是一个内部实现细节。 - Marten

0

你可以通过以下方式获取联系人照片的URI,而无需使用ContactsContract.CommonDataKinds.Email.PHOTO_URI: 点击此处阅读完整答案更多

至于获取联系人URI的方法,你可以尝试以下操作:

import android.provider.ContactsContract.PhoneLookup;

public String fetchContactIdFromPhoneNumber(String phoneNumber) {
    Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
        Uri.encode(phoneNumber));
    Cursor cursor = this.getContentResolver().query(uri,
        new String[] { PhoneLookup.DISPLAY_NAME, PhoneLookup._ID },
        null, null, null);

    String contactId = "";

    if (cursor.moveToFirst()) {
        do {
        contactId = cursor.getString(cursor
            .getColumnIndex(PhoneLookup._ID));
        } while (cursor.moveToNext());
    }

    return contactId;
  }

使用以下代码获取电话号码对应的联系人ID:

import android.provider.ContactsContract.PhoneLookup;

public String fetchContactIdFromPhoneNumber(String phoneNumber) {
    Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
        Uri.encode(phoneNumber));
    Cursor cursor = this.getContentResolver().query(uri,
        new String[] { PhoneLookup.DISPLAY_NAME, PhoneLookup._ID },
        null, null, null);

    String contactId = "";

if (cursor.moveToFirst()) {
    do {
    contactId = cursor.getString(cursor
        .getColumnIndex(PhoneLookup._ID));
    } while (cursor.moveToNext());
}

return contactId;

}

使用获得的联系人ID获取联系人照片URI。使用以下代码获取照片URI:

import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;

public Uri getPhotoUri(long contactId) {
    ContentResolver contentResolver = getContentResolver();

    try {
        Cursor cursor = contentResolver
            .query(ContactsContract.Data.CONTENT_URI,
                null,
                ContactsContract.Data.CONTACT_ID
                    + "="
                    + contactId
                    + " AND "

                    + ContactsContract.Data.MIMETYPE
                    + "='"
                    + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
                    + "'", null, null);

        if (cursor != null) {
        if (!cursor.moveToFirst()) {
            return null; // no photo
        }
        } else {
        return null; // error in cursor process
        }

    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }

    Uri person = ContentUris.withAppendedId(
        ContactsContract.Contacts.CONTENT_URI, contactId);
    return Uri.withAppendedPath(person,
        ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
  }

请告知我,如果这个回答没有解决你的问题。

我想设置联系人照片的URI。 - Guy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接