Добавление миниатюры фотографии к существующему контакту Android без фотографии - PullRequest
0 голосов
/ 07 июня 2018

Я программно создал контакт в адресной книге Android, в котором есть имя и номер телефона.Теперь я хотел бы добавить фотографию к этому контакту, но я не могу заставить это работать.Я не получаю сообщение об ошибке, но фотография, кажется, не добавляется.

Если я создаю новый контакт с фотографией, фотография добавляется правильно, и я могу обновить существующую фотографию этойсвязаться также.Единственная проблема - это контакт без фотографии.

Я уже прочитал несколько решений здесь и здесь , и попробовал несколько вариантов на основе этих ответов, но ни один из них не работает.Мое лучшее предположение, что я делаю что-то не так с 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 и именем MY_ACCOUNT_NAME.Следовательно, builder.withSelection(BASIC_SELECTION для каждой операции обновления, в которой вместо идентификатора используется MY_ACCOUNT_TYPE.

Я также попытался добавить этот же выбор к части «обновить фотографию на существующем контакте», чтобы реализовать ее больше какдругой код «обновления», потому что я хочу быть уверенным, что обновляю фотографию ТОЛЬКО для этого единственного контакта:

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, поэтому я ожидаю, что таким образом будет обновлен правильный контакт.

В случае, если кто-то задается вопросом, какой (статический) импорт используется для 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 голосов
/ 26 июня 2018

С большим количеством проб и ошибок я обнаружил, что RawContactId был неправильным.Наконец, я пришел к такому решению: извлечь RawContacts.CONTACT_ID с помощью PhoneLookup._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));
}
0 голосов
/ 19 июня 2018

Не обращайтесь к CommonDataKinds.Photo напрямую, вместо этого используйте предоставленный API класс помощника DisplayPhoto.Этот вспомогательный класс будет заполнять как PHOTO, так и PHOTO_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);
    }
}
...