Изменение изображения контакта на большую фотографию через PHOTO_FILE_ID в Android

Кажется, это работает для небольших изображений:

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. Как сохранить фото (байт []) и получить PHOTO_FILE_ID?
  2. Если фотография уже доступна (ФОТО не PHOTO_FILE_ID). Нужно ли удалять его, чтобы было видно большое изображение, или большое изображение имеет приоритет, если нет, то как его удалить?

person Guy    schedule 26.04.2016    source источник


Ответы (4)


Ваш собственный ответ будет работать, но он не очень эффективен, потому что фотографию необходимо закодировать в SQL-запрос и передать через Android IPC. Это также делает его предметом ограничения размера IPC для Android, равного 1 МБ (т. е. если ваша фотография слишком велика, операция поставщика содержимого не будет выполнена).

Самый эффективный способ установить (создать или переопределить) фотографию RawContact (основную) — использовать openAssetFileDescriptor и ContactsContract.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 еще нет фотографии, она будет добавлена.

К сожалению, нет способа использовать openAssetFileDescriptor с PHOTO_FILE_ID, поэтому вы не можете переопределить конкретную фотографию, идентифицированную по ее идентификатору, с помощью этого метода. Однако в реальной жизни большинство контактов, вероятно, имеют не более одной фотографии, так что это не является реальным ограничением.

Это автоматически обновит столбец Photo.PHOTO миниатюрой большой фотографии. и назначьте PHOTO_FILE_ID.

person Marten    schedule 29.04.2016
comment
@androiddeveloper нет лучшего способа, который всегда будет работать. Это зависит от вашего варианта использования. Если вы обновляете изображение существующего контакта, вам, вероятно, лучше всего подойдет этот метод (PHOTO_FILE_ID + openAssetFileDescriptor). Иногда предпочтительнее отправлять изображение в пакете операций поставщика контента. Например, он позволяет вам создать контакт, включающий изображение, за одну транзакцию. Мы используем этот метод, когда вставляем новые контакты, но мы также предварительно масштабируем изображения, чтобы убедиться, что они не выйдут за пределы IPC. - person Marten; 04.09.2017
comment
Я имею в виду. Что, если вы запросили контакт (существующий) и получили его идентификатор контакта и ключ поиска. Теперь вы хотите добавить/обновить фотографию. Будет ли этот метод работать всегда (при условии, что вы получите необработанный идентификатор контакта)? Вы написали, что если у него несколько фотографий, это не сработает. Как так? Просто я спросил об этом здесь: stackoverflow.com/q/45894991/878126 и получил другой ответ в коде, и интересно, есть ли лучший способ. - person android developer; 04.09.2017
comment
Если у вас есть необработанный идентификатор существующего контакта, этот метод всегда будет создавать/обновлять (первое) основное фото этого необработанного контакта. Если у необработанного контакта уже есть несколько фотографий, обновляется только первая. Если контакт связан с другими учетными записями, фотографии других необработанных контактов не затрагиваются. Таким образом, если фотография другого связанного необработанного контакта является основной отображаемой фотографией, пользователь не заметит никакой разницы. Если вы хотите убедиться, что пользователь увидит вашу фотографию, вы должны сделать ее суперосновной. - person Marten; 04.09.2017
comment
Возможно ли, что я получаю все необработанные контакты человека, но ни один из них не позволяет обновить/вставить фотографию? Если да, то что нужно делать в этом случае? - person android developer; 05.09.2017
comment
Да, это вполне возможно. Просто представьте, что учетная запись Facebook и WhatsApp связаны без какой-либо учетной записи, доступной для записи. В таком случае вы не сможете заменить фотографию этого контакта. Единственным вариантом было бы добавить еще один необработанный контакт вашего собственного типа учетной записи, связать его с существующим контактом (если это не будет сделано автоматически) и сделать его фотографию суперосновной фотографией. В зависимости от вашего варианта использования вы также можете решить эту проблему, добавив локальный несинхронизированный необработанный контакт. Вероятно, вам следует начать еще один вопрос с изложением вашего варианта использования и фактической проблемы, с которой вы столкнулись. - person Marten; 05.09.2017
comment
Как добавить несинхронизированный необработанный контакт? Кроме того, я создал этот вопрос здесь: stackoverflow.com/q/45894991/878126, как я писал выше. ... :) - person android developer; 06.09.2017
comment
несинхронизированные необработанные контакты (также известные как локальные контакты) имеют тип учетной записи null. Я прочитал ваш вопрос, но на самом деле в нем не было указано, что делает ваше приложение. Возможно, было бы лучше реализовать свой собственный тип учетной записи. Создание местных контактов должно быть только последним средством. - person Marten; 08.09.2017
comment
Я понимаю. Не могли бы вы показать мне, как создать новые данные RawContact только для фотографии локального хранилища с учетом contactId и lookupKey? - person android developer; 09.09.2017
comment
Мы разработали библиотеку под названием ContentPal, которая упрощает разобраться с этими операциями. В нашем репозитории есть несколько ожидающих запросов на вытягивание, которые должны предоставить почти все, что вам нужно. Вы можете открыть вопрос, чтобы получить помощь там. Я не могу описать это в комментарии. Также имейте в виду, что у каждого необработанного контакта должно быть хотя бы имя. - person Marten; 10.09.2017

Наконец удалось решить это с помощью:

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());
 }

}
person Guy    schedule 28.04.2016

PHOTO_FILE_ID — это идентификатор не файла (как ни странно), а строки в базе данных, которая содержит ваши необработанные фотоданные. Согласно документам, которые я просмотрел, вы можете фактически избежать его использования (из документов):

Под PHOTO_FILE_ID

Если он присутствует, он будет использоваться для заполнения PHOTO_URI.

и Под PHOTO_ID (который гарантированно заполняется, если PHOTO_FILE_ID существует)

Ссылка на строку в таблице данных, содержащую фотографию. На фотографию можно ссылаться либо по идентификатору (это поле), либо по URI (см. PHOTO_THUMBNAIL_URI и PHOTO_URI).

Это означает, что если вы просто используете PHOTO_URI, вы получите такое же результирующее изображение, как если бы вы создали метод openDisplayPhoto. Это также предполагает, что методы URI лучше совместимы со сторонними каталогами, поэтому, вероятно, предпочтительнее работать с

person Nick Cardoso    schedule 29.04.2016
comment
На самом деле PHOTO_FILE_ID — это идентификатор (точнее, имя файла) файла. Это имя файла фотографии, хранящегося под /data/data/com.android.providers.contacts/files/photos. К сожалению, вы не можете записывать фотографии, используя этот идентификатор, вы можете их только читать. - person Marten; 29.04.2016
comment
Может быть, это было непонятно. Я имел в виду, что вы можете использовать PHOTO_URI, с помощью которого вы можете открыть поток вывода, чтобы сохранить ваши новые данные и не беспокоиться об использовании идентификатора напрямую. Документы говорят, что PHOTO_FILE_ID является целым числом, поэтому не может быть именем файла. - person Nick Cardoso; 04.05.2016
comment
Вы не можете писать на PHOTO_URI. Это только для чтения первичной фотографии (агрегированного) контакта. Он не работает на RawContacts Попытка записи в этот URI завершится ошибкой (см. ContactsProvider2.java, строка 8033). Что касается имени файла, вы правы, фактическое имя файла представляет собой десятичную строку представления PHOTO_FILE_ID. Но, в конце концов, это не имеет значения, так как в любом случае это всего лишь внутренняя деталь реализации. - person Marten; 04.05.2016

Вы можете получить 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;
  }

Чтобы получить идентификатор контакта по номеру телефона, используйте следующий код:

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;

}

и используйте полученный идентификатор контакта, чтобы получить 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);
  }

Пожалуйста, дайте мне знать, если это не ответ на ваш вопрос

person Michael    schedule 28.04.2016
comment
Я хочу установить контактное фото uri - person Guy; 28.04.2016