将照片缩略图添加到没有照片的现有Android联系人

时间:2018-06-07 14:34:59

标签: android android-contacts contactscontract

我已经以编程方式在Android地址簿中创建了一个联系人,其中包含姓名和电话号码。现在我想为这个联系人添加一张照片,但我似乎无法使用它。我没有收到错误消息,但照片似乎没有添加。

如果我创建一个新的联系人照片,照片会正确添加,我也可以更新此联系人的现有照片。唯一的问题是没有照片的联系人。

我已经阅读了多个解决方案herehere,并根据这些答案尝试了几种变体,但都不起作用。我最好的猜测是我对RAW_CONTACT_ID做错了。谁能找到我的错误?

private void createNewContact()
{
  ArrayList<ContentProviderOperation> ops = new ArrayList<>();

  ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
  builder.withValue(RawContacts.ACCOUNT_TYPE, MY_ACCOUNT_TYPE);
  builder.withValue(RawContacts.ACCOUNT_NAME, MY_ACCOUNT_NAME);
  ops.add(builder.build());

  builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
  builder.withValueBackReference(Data.RAW_CONTACT_ID, 0);
  builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
  builder.withValue(StructuredName.GIVEN_NAME, myName);
  ops.add(builder.build());

  builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
  builder.withValueBackReference(Data.RAW_CONTACT_ID, 0);
  builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
  builder.withValue(Phone.TYPE, Phone.TYPE_WORK);
  builder.withValue(Phone.NUMBER, myPhoneNumber);
  ops.add(builder.build());

  builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
  builder.withValueBackReference(Data.RAW_CONTACT_ID, 0);
  builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
  builder.withValue(Photo.PHOTO, myPhotoByteArray);
  ops.add(builder.build());

  try
  {
      context.getContentResolver().applyBatch(AUTHORITY, ops);
  }
  catch (Exception e)
  {
      // Handle exception
  }
}



private static final String BASIC_SELECTION = RawContacts.ACCOUNT_TYPE + "='" + MY_ACCOUNT_TYPE + "'";

private void editContact() {
  ArrayList<ContentProviderOperation> ops = new ArrayList<>();

  ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(Data.CONTENT_URI);
  builder.withSelection(BASIC_SELECTION + " AND " + Data.MIMETYPE + "='" + StructuredName.CONTENT_ITEM_TYPE + "'", null);
  builder.withValue(StructuredName.GIVEN_NAME, myName);
  ops.add(builder.build());

  builder = ContentProviderOperation.newUpdate(Data.CONTENT_URI);
  builder.withSelection(BASIC_SELECTION + " AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + " AND " + Phone.TYPE + "='" + Phone.TYPE_WORK + "'", null);
  builder.withValue(Phone.NUMBER, myPhoneNumber);
  ops.add(builder.build());

  if (isContactWithoutPhoto())
  {
    // Contact without photo: insert new photo
    // TODO: this part doesn't work yet!
    int rawContactId = getRawContactId();
    if (rawContactId != -1)
    {
        builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
        builder.withValue(Data.RAW_CONTACT_ID, rawContactId);
        builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
        builder.withValue(Photo.PHOTO, myPhotoByteArray);
        ops.add(builder.build());
    }
  }
  else
  {
    // Contact already has a photo: update
    builder = ContentProviderOperation.newUpdate(Data.CONTENT_URI);
    builder.withSelection(BASIC_SELECTION + " AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'", null);
    builder.withValue(Photo.PHOTO, myPhotoBytes);
    ops.add(builder.build());
  }

  try
  {
      context.getContentResolver().applyBatch(AUTHORITY, ops);
  }
  catch (Exception e)
  {
      // Handle exception
  }
}

private int getRawContactId()
{
    int rawContactId = -1;
    Cursor cursor = null;
    try
    {
        cursor = App.getContext().getContentResolver().query(RawContacts.CONTENT_URI, null, ACCOUNT_TYPE_SELECTION, null, null);
        if (cursor != null && cursor.moveToFirst())
        {
            rawContactId = cursor.getInt(cursor.getColumnIndex(RawContacts._ID));
        }
    }
    finally
    {
        if (cursor != null)
        {
            cursor.close();
        }
    }
    return rawContactId;
}

修改

我只需要一次创建一个联系人,之后只需要定期更新。此联系人保存在自行创建的帐户中,类型为MY_ACCOUNT_TYPE&amp;将MY_ACCOUNT_NAME命名为。因此,每个更新操作的builder.withSelection(BASIC_SELECTION使用MY_ACCOUNT_TYPE而不是id。

我还尝试在现有联系人的“更新照片”上添加相同的选择。部分,实现它更像其他更新&#39;代码,因为我想确保我只为这个单一联系人更新照片:

int rawContactId = getRawContactId();
if (rawContactId != -1)
{
    builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
    builder.withSelection(BASIC_SELECTION + " AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'", null);
    builder.withValue(Data.RAW_CONTACT_ID, rawContactId);
    builder.withValue(Photo.PHOTO, myPhotoByteArray);
    ops.add(builder.build());

在这种情况下,应用程序显然会因错误java.lang.IllegalArgumentException: only updates, deletes, and asserts can have selections而崩溃。已使用BASIC_SELECTION收集了ID,因此我希望无论如何都能以这种方式更新正确的联系人。

如果有人想知道使用了ContactsContract的哪些(静态)导入:

import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.PhoneLookup;

import static android.provider.ContactsContract.AUTHORITY;
import static android.provider.ContactsContract.CommonDataKinds.Note;
import static android.provider.ContactsContract.CommonDataKinds.Phone;
import static android.provider.ContactsContract.CommonDataKinds.StructuredName;
import static android.provider.ContactsContract.Data;
import static android.provider.ContactsContract.RawContacts;

2 个答案:

答案 0 :(得分:0)

不要直接访问CommonDataKinds.Photo,而是使用API​​提供的帮助程序类DisplayPhoto。 这个助手类将填充PHOTOPHOTO_FILE_ID,这是您的代码所缺少的。

尝试以下方法:

public void setPhoto(long rawContactId, byte[] photo) {
    Uri rawContactPhotoUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
    try {
        AssetFileDescriptor fd = contentResolver.openAssetFileDescriptor(rawContactPhotoUri, "rw");
        OutputStream os = fd.createOutputStream();
        os.write(photo);
        os.close();
        fd.close();
    } catch (IOException e) {
        Log.e(TAG, "error", e);
    }
}

答案 1 :(得分:0)

经过反复试验,我发现RawContactId不正确。 最后,我想到了这个解决方案:使用PhoneLookup._ID检索RawContacts.CONTACT_ID,可以通过phoneNumber进行检索。

// This code stays the same as described in the question (except for some error handling and closing cursors, which I left out for simplicity sake):

private void createNewContact() {
  ...
}

private static final String BASIC_SELECTION = RawContacts.ACCOUNT_TYPE + "='" + MY_ACCOUNT_TYPE + "'";

private void editContact() {
  ...
  int rawContactId = getRawContactId();
  ...
}

// The different part is the way to retrieve the RawContactId:

private static final ContentResolver CONTENT_RESOLVER = context.getContentResolver();

private int getRawContactId()
{
    String[] projection = {Phone.NUMBER};
    Cursor cursor = CONTENT_RESOLVER.query(Phone.CONTENT_URI, projection, BASIC_SELECTION, null, null);
    String phoneNumber = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
    return getRawContactIdWithPhoneNumber(phoneNumber);
}

private int getRawContactIdWithPhoneNumber(String phoneNumber)
{
    Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
    String[] projection = {PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
    Cursor cursor = CONTENT_RESOLVER.query(uri, projection, BASIC_SELECTION, null, null);
    int id = cursor.getInt(cursor.getColumnIndex(PhoneLookup._ID));
    return getRawContactIdWithPhoneLookupId(id);
}

private int getRawContactIdWithPhoneLookupId(int id)
{
    String[] projection = new String[]{RawContacts._ID};
    String selection = RawContacts.CONTACT_ID + "=?";
    String[] selectionArgs = new String[]{String.valueOf(id)};
    Cursor cursor = CONTENT_RESOLVER.query(RawContacts.CONTENT_URI, projection, selection, selectionArgs, null);
    return cursor.getInt(cursor.getColumnIndex(RawContacts._ID));
}