blob: 4d07856f250ed4252dca47b98ba925294c6f9b18 [file] [log] [blame]
page.title=Поставщик контактов
@jd:body
<div id="qv-wrapper">
<div id="qv">
<h2>Краткое описание</h2>
<ul>
<li>Репозиторий Android с пользовательскими данными.</li>
<li>
Синхронизация со службами в Интернете.
</li>
<li>
Интеграция с потоками данных из социальных сетей.
</li>
</ul>
<h2>Содержание документа</h2>
<ol>
<li>
<a href="#InformationTypes">Структура поставщика контактов</a>
</li>
<li>
<a href="#RawContactBasics">Необработанные контакты</a>
</li>
<li>
<a href="#DataBasics">Данные</a>
</li>
<li>
<a href="#ContactBasics">Контакты</a>
</li>
<li>
<a href="#Sources">Данные, полученные от адаптеров синхронизации</a>
</li>
<li>
<a href="#Permissions">Требуемые разрешения</a>
</li>
<li>
<a href="#UserProfile">Профиль пользователя</a>
</li>
<li>
<a href="#ContactsProviderMetadata">Метаданные поставщика контактов</a>
</li>
<li>
<a href="#Access">Доступ к поставщику контактов</a>
<li>
</li>
<li>
<a href="#SyncAdapters">Адаптеры синхронизации поставщика контактов</a>
</li>
<li>
<a href="#SocialStream">Потоки данных из социальных сетей</a>
</li>
<li>
<a href="#AdditionalFeatures">Дополнительные возможности поставщика контактов</a>
</li>
</ol>
<h2>Ключевые классы</h2>
<ol>
<li>{@link android.provider.ContactsContract.Contacts}</li>
<li>{@link android.provider.ContactsContract.RawContacts}</li>
<li>{@link android.provider.ContactsContract.Data}</li>
<li>{@code android.provider.ContactsContract.StreamItems}</li>
</ol>
<h2>Образцы кода по теме</h2>
<ol>
<li>
<a href="{@docRoot}resources/samples/ContactManager/index.html">
Диспетчер контактов
</a>
</li>
<li>
<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">
Пример адаптера синхронизации</a>
</li>
</ol>
<h2>См. также:</h2>
<ol>
<li>
<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
Основные сведения о поставщике контента
</a>
</li>
</ol>
</div>
</div>
<p>
Поставщик контактов представляет собой эффективный и гибкий компонент Android, который управляет
центральным репозиторием устройства, в котором хранятся пользовательские данные. Поставщик контактов — это источник данных,
которые отображаются в приложении «Контакты» на вашем устройстве. Вы также можете получить доступ к этим данным в своем собственном
приложении и организовать обмен такими данными между устройством и службами в Интернете. Поставщик взаимодействует
с широким набором источников данных и пытается организовать управление как можно большим набором данных о каждом человеке, поэтому
организация поставщика довольно сложная. По этой причине API поставщика
содержит широкий набор классов-контрактов и интерфейсов, отвечающих как за получение данных, так и за их
изменение.
</p>
<p>
В этом руководстве рассматриваются следующие вопросы:
</p>
<ul>
<li>
основная структура поставщика;
</li>
<li>
порядок получения данных от поставщика;
</li>
<li>
порядок изменения данных в поставщике;
</li>
<li>
порядок записи адаптера синхронизации для синхронизации данных, полученных с вашего сервера, с данными в
поставщике контактов.
</li>
</ul>
<p>
При изучении данного материала подразумевается, что вы уже знакомы с основами поставщиков контента Android. Дополнительные сведения
о поставщиках контента Android представлены в руководстве
<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">Основные
сведения о поставщике контента</a>. Пример
<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">адаптера синхронизации</a>
служит примером использования такого приложения для обмена данными между поставщиком
контактов и приложением, размещенным в веб-службах Google.
</p>
<h2 id="InformationTypes">Структура поставщика контактов</h2>
<p>
Поставщик контактов представляет собой поставщик контента Android. Он содержит в себе три типа
данных о пользователе, каждый из которых указан в отдельной таблице, предоставляемой поставщиком,
как показано на рисунке 1.
</p>
<img src="{@docRoot}images/providers/contacts_structure.png" alt="" height="364" id="figure1" />
<p class="img-caption">
<strong>Рисунок 1.</strong> Структура таблицы поставщика контактов.
</p>
<p>
В качестве имен этих трех таблиц обычно используются названия соответствующих классов-контрактов. Эти классы
определяют константы для URI контента, названий столбцов и значений в столбцах этих таблиц.
</p>
<dl>
<dt>
Таблица {@link android.provider.ContactsContract.Contacts}
</dt>
<dd>
Строки в этой таблице содержат данные о разных пользователях, полученные путем агрегации строк необработанных контактов.
</dd>
<dt>
Таблица {@link android.provider.ContactsContract.RawContacts}
</dt>
<dd>
Строки в этой таблице содержат сводные данные о пользователе, относящиеся к пользовательскому аккаунту и его типу.
</dd>
<dt>
Таблица {@link android.provider.ContactsContract.Data}
</dt>
<dd>
Строки в этой таблице содержат сведения о необработанных контактах, такие как адреса эл. почты или номера телефонов.
</dd>
</dl>
<p>
Другие таблицы, представленные классами-контрактами в {@link android.provider.ContactsContract},
представляют собой вспомогательные таблицы, которые поставщик контактов использует для управления своими операциями или поддержки
определенных функций, имеющихся в приложении устройства «Контакты» или приложениях для телефонной связи.
</p>
<h2 id="RawContactBasics">Необработанные контакты</h2>
<p>
Необработанный контакт представляет собой данные о пользователе, поступающие из одного аккаунта определенного
типа. Поскольку в качестве источника данных о пользователе в поставщике контактов может выступать сразу несколько онлайн-служб,
для одного и того же пользователя в поставщике контактов может существовать несколько необработанных контактов.
Это также позволяет пользователю объединять пользовательские данные из нескольких аккаунтов
одного и того же типа.
</p>
<p>
Большая часть данных необработанного контакта не хранится в таблице
{@link android.provider.ContactsContract.RawContacts}. Вместо этого они хранятся в одной или нескольких
строках в таблице {@link android.provider.ContactsContract.Data}. В каждой строке данных имеется
столбец {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID},
в котором содержится значение {@code android.provider.BaseColumns#_ID RawContacts._ID} его
родительской строки{@link android.provider.ContactsContract.RawContacts}.
</p>
<h3 id="RawContactsColumns">Важные столбцы необработанных контактов</h3>
<p>
В таблице 1 указаны столбцы таблицы {@link android.provider.ContactsContract.RawContacts},
которые имеют большое значение. Обязательно ознакомьтесь с примечаниями, приведенными после этой таблицы.
</p>
<p class="table-caption" id="table1">
<strong>Таблица 1.</strong> Важные столбцы необработанных контактов.
</p>
<table>
<tr>
<th scope="col">Название столбца</th>
<th scope="col">Использование</th>
<th scope="col">Примечания</th>
</tr>
<tr>
<td>
{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME}
</td>
<td>
Имя аккаунта для типа аккаунта, который выступает в роли источника данных для необработанного контакта.
Например, имя аккаунта Google является одним из эл. адресов почты Gmail
владельца устройства. Дополнительные сведения
см. в следующей записи
{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE}.
</td>
<td>
Формат этого имени зависит от типа аккаунта. Это не обязательно
адрес эл. почты.
</td>
</tr>
<tr>
<td>
{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE}
</td>
<td>
Тип аккаунта, который выступает в роли источника данных для необработанного контакта. Например, тип аккаунта
Google — <code>com.google</code>. Всегда указывайте тип аккаунта
и идентификатор домена, которым вы владеете или управляете. Это позволит гарантировать уникальность
типа вашего аккаунта.
</td>
<td>
У типа аккаунта, выступающего в роли источника данных контактов, обычно имеется связанный с ним адаптер синхронизации,
который синхронизирует данные с поставщиком контактов.
</tr>
<tr>
<td>
{@link android.provider.ContactsContract.RawContactsColumns#DELETED}
</td>
<td>
Флаг deleted для необработанного контакта.
</td>
<td>
Этот флаг позволяет поставщику контактов хранить строку внутри себя до тех пор,
пока адаптеры синхронизации не смогут удалить эту строку с серверов, а затем
удалить ее из репозитория.
</td>
</tr>
</table>
<h4>Примечания</h4>
<p>
Ниже представлены важные примечания к таблице
{@link android.provider.ContactsContract.RawContacts}.
</p>
<ul>
<li>
Имя необработанного контакта не хранится в его строке в таблице
{@link android.provider.ContactsContract.RawContacts}. Вместо этого оно хранится в
таблице {@link android.provider.ContactsContract.Data}
в строке {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}. У необработанного контакта
имеется в таблице {@link android.provider.ContactsContract.Data} только одна строка такого типа.
</li>
<li>
<strong>Внимание!</strong> Чтобы использовать в строке необработанного контакта данные собственного аккаунта, строку
, сначала необходимо зарегистрировать его в классе {@link android.accounts.AccountManager}. Для этого предложите
пользователям добавить тип аккаунта и его имя в список аккаунтов. Если
не сделать этого, поставщик контактов автоматически удалит вашу строку необработанного контакта.
<p>
Например, если необходимо, чтобы в вашем приложении хранились данные контактов для веб-службы
в домене {@code com.example.dataservice}, и аккаунт пользователя службы
выглядит следующим образом:{@code becky.sharp@dataservice.example.com}, то пользователю сначала необходимо добавить
«тип» аккаунта ({@code com.example.dataservice}) и его «имя»
({@code becky.smart@dataservice.example.com}) прежде чем ваше приложение сможет добавлять строки необработанных контактов.
Это требование можно указать в документации для пользователей, а также можно предложить
пользователю добавить тип и имя своего аккаунта, либо реализовать оба этих варианта. Более подробно типы и имена аккаунтов
рассматриваются в следующем разделе.
</li>
</ul>
<h3 id="RawContactsExample">Источники данных необработанных контактов</h3>
<p>
Чтобы понять, что такое необработанный контакт, рассмотрим пример с пользователем Emily Dickinson, на устройстве которой имеется три
следующих аккаунта:
</p>
<ul>
<li><code>emily.dickinson@gmail.com</code>;</li>
<li><code>emilyd@gmail.com</code>;</li>
<li>Аккаунт belle_of_amherst в Twitter</li>
</ul>
<p>
Она включила функцию <em>Синхронизировать контакты</em> для всех трех этих аккаунтов
в настройках <em>Аккаунты</em>.
</p>
<p>
Предположим, что Emily Dickinson открывает браузер, входит в Gmail под именем
<code>emily.dickinson@gmail.com</code>, затем открывает
Контакты и добавляет новый контакт Thomas Higginson. Позже она снова входит в Gmail под именем
<code>emilyd@gmail.com</code> и отправляет письмо пользователю Thomas Higginson, который автоматически
добавляется в ее контакты. Также она подписана на новости от colonel_tom (аккаунт пользователя Thomas Higginson в Twitter) в
Twitter.
</p>
<p>
В результате этих действий поставщик контактов создает три необработанных контакта:
</p>
<ol>
<li>
Необработанный контакт Thomas Higginson связан с аккаунтом <code>emily.dickinson@gmail.com</code>.
Тип этого аккаунта — Google.
</li>
<li>
Второй необработанный контакт Thomas Higginson связан с аккаунтом <code>emilyd@gmail.com</code>.
Тип этого аккаунта также Google. Второй необработанный контакт создается даже в том случае,
если его имя полностью совпадает с именем предыдущего контакта, поскольку первый
был добавлен для другого аккаунта.
</li>
<li>
Третий необработанный контакт Thomas Higginson связан с аккаунтом belle_of_amherst. Тип этого аккаунта
Twitter.
</li>
</ol>
<h2 id="DataBasics">Данные</h2>
<p>
Как уже указывалось ранее, данные необработанного контакта
хранятся в строке {@link android.provider.ContactsContract.Data}, которая связана со значением
<code>_ID</code> необработанного контакта. Благодаря этому для одного необработанного контакта может существовать несколько экземпляров
одного и того же типа данных (например, адресов эл. почты или номеров телефонов). Например, если у контакта
Thomas Higginson для аккаунта {@code emilyd@gmail.com} (строка необработанного контакта Thomas Higginson,
связанная с учетной записью Google <code>emilyd@gmail.com</code>) имеется адрес эл. почты
<code>thigg@gmail.com</code> и служебный адрес эл. почты
<code>thomas.higginson@gmail.com</code>, то поставщик контактов сохраняет две строки
адреса эл. почты и связывает их с этим необработанным контактом.
</p>
<p>
Обратите внимание, что в этой таблице хранятся данные разных типов. Строки со сведениями об отображаемом имени,
номере телефона, адресе эл. почты, почтовом адресе, фото и веб-сайте хранятся в таблице
{@link android.provider.ContactsContract.Data}. Для упрощения управления этими данными в таблице
{@link android.provider.ContactsContract.Data} предусмотрены столбцы с описательными именами,
а также другие столбцы с универсальными именами. Содержимое в столбце с описательным именем имеет то же значение,
независимо от типа данных в строке, тогда как содержимое столбца с универсальным именем
может иметь разное значение в зависимости от типа данных.
</p>
<h3 id="DescriptiveColumns">Описательные имена столбцов</h3>
<p>
Вот некоторые примеры столбцов с описательными именами:
</p>
<dl>
<dt>
{@link android.provider.ContactsContract.Data#RAW_CONTACT_ID}
</dt>
<dd>
Значение в столбце <code>_ID</code> необработанного контакта для этих данных.
</dd>
<dt>
{@link android.provider.ContactsContract.Data#MIMETYPE}
</dt>
<dd>
Тип данных, хранящихся в этой строке, в виде настраиваемого типа MIME. Поставщик контактов
использует типы MIME, заданные в подклассах класса
{@link android.provider.ContactsContract.CommonDataKinds}. Эти типы MIME являются открытыми, и
их может использовать любое приложение или адаптер синхронизации, поддерживающие работу с поставщиком контактов.
</dd>
<dt>
{@link android.provider.ContactsContract.DataColumns#IS_PRIMARY}
</dt>
<dd>
Если этот тип данных для необработанного контакта встречается несколько раз, то
столбец {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} помечает флагом
строки данных, в которых содержатся основные данные для этого типа. Например, если
пользователь нажал и удерживает номер телефона контакта и выбрал параметр <strong>Использовать по умолчанию</strong>,
то в столбце {@link android.provider.ContactsContract.Data} с этим номером телефона
в соответствующем столбце {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} задается
значение, отличное от нуля.
</dd>
</dl>
<h3 id="GenericColumns">Универсальные имена столбцов</h3>
<p>
Существует 15 общедоступных столбцов с универсальными именами (<code>DATA1</code>–<code>DATA15</code>)
и четыре дополнительных столбца
(<code>SYNC1</code>–<code>SYNC4</code>), которые используются только адаптерами
синхронизации. Константы столбцов с универсальными именами применяются всегда, независимо от типа данных,
содержащихся в столбце.
</p>
<p>
Столбец <code>DATA1</code> является индексируемым. Поставщик контактов всегда использует этот столбец для
данных, которые, как он ожидает, будут наиболее часто являться целевыми в запросах. Например,
в строке с адресами эл. почты в этом столбце указывается фактический адрес эл. почты.
</p>
<p>
Обычно столбец <code>DATA15</code> зарезервирован для данных больших двоичных объектов
(BLOB), таких как миниатюры фотографий.
</p>
<h3 id="TypeSpecificNames">Имена столбцов по типам строк</h3>
<p>
Для упрощения работы со столбцами определенного типа строк в поставщике контактов
также предусмотрены константы для имен столбцов по типам строк, которые определены в подклассах класса
{@link android.provider.ContactsContract.CommonDataKinds}. Эти константы просто
присваивают одному и тому же имени столбца различные константы, что позволяет вам получать доступ к данным в строке
определенного типа.
</p>
<p>
Например, класс {@link android.provider.ContactsContract.CommonDataKinds.Email} определяет
константы имени столбца для строки {@link android.provider.ContactsContract.Data},
в которой имеется тип MIME
{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
Email.CONTENT_ITEM_TYPE}. В этом классе содержится константа
{@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} для столбца адреса
эл. почты. Фактическое значение
{@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} — data1, которое совпадает
с универсальным именем столбца.
</p>
<p class="caution">
<strong>Внимание!</strong> Не добавляйте свои настраиваемые данные в таблицу
{@link android.provider.ContactsContract.Data} с помощью строки, в которой имеется один из
предварительно заданных поставщиком типов MIME. В противном случае вы можете потерять данные или вызвать неполадки
в работе поставщика. Например, не следует добавлять строку с типом MIME
{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
Email.CONTENT_ITEM_TYPE}, в которой в столбце
<code>DATA1</code> вместо адреса эл. почты содержится имя пользователя. Если вы укажете в строке собственный настраиваемый тип MIME, вы можете свободно
указывать собственные имена столбцов по типам строк и использовать их так, как пожелаете.
</p>
<p>
На рисунке 2 показано, как столбцы с описательными именами и столбцы данных отображаются в строке
{@link android.provider.ContactsContract.Data}, а также как имена столбцов по типу строк «накладываются»
на универсальные имена столбцов.
</p>
<img src="{@docRoot}images/providers/data_columns.png" alt="How type-specific column names map to generic column names" height="311" id="figure2" />
<p class="img-caption">
<strong>Рисунок 2.</strong> Имена столбцов по типам строк и универсальные имена столбцов.
</p>
<h3 id="ColumnMaps">Классы имен столбцов по типам строк</h3>
<p>
В таблице 2 перечислены наиболее часто используемые классы имен столбцов по типам строк.
</p>
<p class="table-caption" id="table2">
<strong>Таблица 2.</strong> Классы имен столбцов по типам строк</p>
<table>
<tr>
<th scope="col">Класс сопоставления</th>
<th scope="col">Тип данных</th>
<th scope="col">Примечания</th>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.CommonDataKinds.StructuredName}</td>
<td>Данные об имени необработанного контакта, связанного с этой строкой данных.</td>
<td>У необработанного контакта имеется только одна строка такого типа.</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.CommonDataKinds.Photo}</td>
<td>Основная фотография необработанного контакта, связанного с этой строкой данных.</td>
<td>У необработанного контакта имеется только одна строка такого типа.</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.CommonDataKinds.Email}</td>
<td>Адрес эл. почты необработанного контакта, связанного с этой строкой данных.</td>
<td>У необработанного контакта может быть несколько адресов эл. почты.</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal}</td>
<td>Почтовый адрес необработанного контакта, связанного с этой строкой данных.</td>
<td>У необработанного контакта может быть несколько почтовых адресов.</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}</td>
<td>Идентификатор, с помощью которого необработанный контакт связан с одной из групп в поставщике контактов.</td>
<td>
Группы представляют собой необязательный компонент для типа аккаунта и имени аккаунта. Дополнительные сведения о
группах представлены в разделе <a href="#Groups">Группы контактов</a>.
</td>
</tr>
</table>
<h3 id="ContactBasics">Контакты</h3>
<p>
Поставщик контактов объединяет строки с необработанными контактами для всех типов аккаунтов и имен аккаунтов
для создания <strong>контакта</strong>. Это позволяет упростить отображение и изменение всех данных,
собранных в отношении пользователя. Поставщик контактов управляет процессом создания строк
контактов и агрегированием необработанных контактов, у которых имеется строка контакта. Ни приложениям, ни
адаптерам синхронизации не разрешается добавлять контакты, а некоторые столбцы в строке контакта доступны только для чтения.
</p>
<p class="note">
<strong>Примечание.</strong> Если попытаться добавить контакт в поставщик контактов с помощью метода
{@link android.content.ContentResolver#insert(Uri,ContentValues) insert()}, будет выдано исключение
{@link java.lang.UnsupportedOperationException}. Если вы попытаетесь обновить столбец,
доступный только для чтения, это действие будет проигнорировано.
</p>
<p>
При добавлении нового необработанного контакта,
который не соответствует ни одному из существующих контактов, поставщик контактов создает новый контакт. Поставщик поступает аналогично в случае, если
данные в строке существующего необработанного контакта изменяются таким образом, что они больше не соответствуют контакту,
с которым они ранее были связаны. При создании приложением или адаптером синхронизации нового контакта,
который <em></em> соответствует существующему контакту, то новый контакт объединяется с
существующим контактом.
</p>
<p>
Поставщик контактов связывает строку контакта с его строками необработанного контакта посредством столбца
<code>_ID</code> строки контакта в таблице
{@link android.provider.ContactsContract.Contacts Contacts}. Столбец <code>CONTACT_ID</code> в таблице необработанных контактов
{@link android.provider.ContactsContract.RawContacts} содержит значения <code>_ID</code> для
строки контактов, связанной с каждой строкой необработанных контактов.
</p>
<p>
В таблице {@link android.provider.ContactsContract.Contacts} также имеется столбец
{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY}, который выступает в роли
«постоянной ссылки» на строку контакта. Поскольку поставщик контактов автоматически сохраняет контакты,
в ответ на агрегирование или синхронизацию он может изменить значение {@code android.provider.BaseColumns#_ID}
строки контакта. Даже если это произойдет, URI контента
{@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, объединенный с
{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} контакта, будет
по-прежнему указывать на строку контакта, поэтому вы можете смело использовать
{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY}
для сохранения ссылок на «избранные» контакты и др. Столбец имеет собственный формат,
который не связан с форматом столбца{@code android.provider.BaseColumns#_ID}.
</p>
<p>
На рисунке 3 показаны взаимосвязи этих трех основных таблиц друг с другом.
</p>
<img src="{@docRoot}images/providers/contacts_tables.png" alt="Contacts provider main tables" height="514" id="figure4" />
<p class="img-caption">
<strong>Рисунок 3</strong>. Взаимосвязи между таблицами контактов, необработанных контактов и сведений.
</p>
<h2 id="Sources">Данные, полученные от адаптеров синхронизации</h2>
<p>
Пользователь вводит данные контактов прямо на устройстве, однако данные также поступают в поставщик контактов
из веб-служб посредством <strong>адаптеров синхронизации</strong>, что позволяет автоматизировать
обмен данными между устройством и службами в Интернете. Адаптеры синхронизации выполняются в фоновом режиме под
управлением системы, и для управления данными они вызывают методы
{@link android.content.ContentResolver}.
</p>
<p>
В Android веб-служба, с которой работает адаптер синхронизации, определяется по типу аккаунта.
Каждый адаптер синхронизации работает с одним типом аккаунта, однако он может поддерживать несколько имен аккаунтов
такого типа. Вкратце типы и имена аккаунтов рассматриваются
в разделе <a href="#RawContactsExample">Источники данных необработанных контактов</a>. Указанные ниже определения позволяют
более точно охарактеризовать связь между типами и именами аккаунтов и адаптерами синхронизации и службами.
</p>
<dl>
<dt>
Тип аккаунта
</dt>
<dd>
Определяет службу, в которой пользователь хранит данные. В большинстве случаев для работы со
службой пользователю необходимо пройти проверку подлинности. Например, Google Контакты представляет собой тип аккаунтов, обозначаемый кодом
<code>google.com</code>. Это значение соответствует типу аккаунта, используемого
{@link android.accounts.AccountManager}.
</dd>
<dt>
Имя аккаунта
</dt>
<dd>
Определяет конкретный аккаунт или имя для входа для типа аккаунта. Аккаунты «Контакты Google»
это то же, что и аккаунты Google, в качестве имени которых используется адрес эл. почты.
В других службах может использоваться имя пользователя, состоящее из одного слова, или числовой идентификатор.
</dd>
</dl>
<p>
Типы аккаунтов не обязательно должны быть уникальными. Пользователь может создать несколько аккаунтов Google Контакты
и загрузить данные из них в поставщик контактов; это может произойти в случае, если у пользователя имеется один набор
персональных контактов для личного аккаунта, и другой набор — для служебного аккаунта. Имена
аккаунтов обычно уникальные. Вместе они формируют определенный поток данных между поставщиком контактов
и внешними службами.
</p>
<p>
Если необходимо передать данные из службы в поставщик контактов, необходимо создать
собственный адаптер синхронизации. Дополнительные сведения об этом представлены в разделе
<a href="#SyncAdapters">Адаптеры синхронизации поставщика контактов</a>.
</p>
<p>
На рисунке 4 показано, какую роль выполняет поставщик контактов в потоке передачи данных
о пользователях. В области, отмеченной на рисунке как sync adapters, каждый адаптер промаркирован в соответствии с поддерживаемым им типом аккаунтов.
</p>
<img src="{@docRoot}images/providers/ContactsDataFlow.png" alt="Flow of data about people" height="252" id="figure5" />
<p class="img-caption">
<strong>Рисунок 4</strong>. Поток передачи данных через поставщика контактов.
</p>
<h2 id="Permissions">Требуемые разрешения</h2>
<p>
Приложения, которым требуется доступ к поставщику контактов,
должны запросить указанные ниже разрешения.
</p>
<dl>
<dt>Доступ на чтение одной или нескольких таблиц</dt>
<dd>
{@link android.Manifest.permission#READ_CONTACTS}, указанный в файле
<code>AndroidManifest.xml</code> с элементом
<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">
&lt;uses-permission&gt;</a></code> в виде
<code>&lt;uses-permission android:name="android.permission.READ_CONTACTS"&gt;</code>.
</dd>
<dt>Доступ на запись в одну или несколько таблиц</dt>
<dd>
{@link android.Manifest.permission#WRITE_CONTACTS}, указанный в файле
<code>AndroidManifest.xml</code> с элементом
<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">
&lt;uses-permission&gt;</a></code> в виде
<code>&lt;uses-permission android:name="android.permission.WRITE_CONTACTS"&gt;</code>.
</dd>
</dl>
<p>
Эти разрешения не распространяются на работу с данными профиля пользователя. Профиль пользователя
и требуемые разрешения для работы с ним рассматриваются в разделе
<a href="#UserProfile">Профиль пользователя</a> ниже.
</p>
<p>
Помните, что данные о контактах пользователя являются личными и конфиденциальными. Пользователи заботятся о своей конфиденциальности,
поэтому не хотят, чтобы приложения собирали данные о них самих или их контактах.
Если пользователю не до конца ясно, для чего требуется разрешение на доступ к данным контактов, они могут присвоить
вашему приложению низкий рейтинг или вообще откажутся его устанавливать.
</p>
<h2 id="UserProfile">Профиль пользователя</h2>
<p>
В таблице {@link android.provider.ContactsContract.Contacts} имеется всего одна строка, содержащая
данные профиля для устройства пользователя. Эти данные описывают <code>user</code> устройства,
а не один из контактов пользователя. Строка контактов профиля связана со строкой
необработанных контактов в каждой из систем, в которой используется профиль.
В каждой строке необработанного контакта профиля может содержаться несколько строк данных. Константы для доступа к профилю пользователя
представлены в классе {@link android.provider.ContactsContract.Profile}.
</p>
<p>
Для доступа к профилю пользователя требуются особые разрешения. Кроме разрешений
{@link android.Manifest.permission#READ_CONTACTS} и
{@link android.Manifest.permission#WRITE_CONTACTS}, которые требуются для чтения и записи, для доступа к профилю пользователя необходимы разрешения
{@code android.Manifest.permission#READ_PROFILE} и
{@code android.Manifest.permission#WRITE_PROFILE}
на чтение и запись соответственно.
</p>
<p>
Всегда следует помнить, что профиль пользователя представляет собой конфиденциальную информацию. Разрешение
{@code android.Manifest.permission#READ_PROFILE} предоставляет вам доступ к личной информации на устройстве
пользователя. В описании своего приложения обязательно укажите, для
чего вам требуется доступ к профилю пользователя.
</p>
<p>
Чтобы получить строку контакта с профилем пользователя, вызовите метод
{@link android.content.ContentResolver#query(Uri,String[], String, String[], String)
ContentResolver.query()}. Задайте для URI контента значение
{@link android.provider.ContactsContract.Profile#CONTENT_URI} и не указывайте никаких критериев
выборки. Этот URI контента также можно использовать в качестве основного URI для получения
необработанных контактов или данных профиля. Ниже представлен фрагмент кода для получения данных профиля.
</p>
<pre>
// Sets the columns to retrieve for the user profile
mProjection = new String[]
{
Profile._ID,
Profile.DISPLAY_NAME_PRIMARY,
Profile.LOOKUP_KEY,
Profile.PHOTO_THUMBNAIL_URI
};
// Retrieves the profile from the Contacts Provider
mProfileCursor =
getContentResolver().query(
Profile.CONTENT_URI,
mProjection ,
null,
null,
null);
</pre>
<p class="note">
<strong>Примечание.</strong> Если при извлечении несколько строк контактов необходимо определить,
какой из них является профилем пользователя, обратитесь к столбцу
{@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} этой строки. Если в этом столбце
указано значение «1», то в это строке находится профиль пользователя.
</p>
<h2 id="ContactsProviderMetadata">Метаданные поставщика контактов</h2>
<p>
Поставщик контактов управляет данными, которые используются для отслеживания состояния данных контакта в
репозитории. Эти метаданные о репозитории хранятся в различных местах, включая
строки необработанных контактов, данные и строки таблицы контактов, таблицу
{@link android.provider.ContactsContract.Settings} и таблицу
{@link android.provider.ContactsContract.SyncState}. В таблице ниже
указаны значения каждого из этих фрагментов метаданных.
</p>
<p class="table-caption" id="table3">
<strong>Таблица 3.</strong> Метаданные в поставщике контактов</p>
<table>
<tr>
<th scope="col">Таблица</th>
<th scope="col">Столбец</th>
<th scope="col">Значения</th>
<th scope="col">Описание</th>
</tr>
<tr>
<td rowspan="2">{@link android.provider.ContactsContract.RawContacts}</td>
<td rowspan="2">{@link android.provider.ContactsContract.SyncColumns#DIRTY}</td>
<td>«0» — с момента последней синхронизации изменения не вносились.</td>
<td rowspan="2">
Служит для отметки необработанных контактов, которые были изменены на устройстве и которые необходимо снова синхронизировать с
сервером. Значение устанавливается автоматически поставщиком контактов при обновлении строк приложениями
Android.
<p>
Адаптеры синхронизации, которые вносят изменения в необработанные контакты или таблицы данных, должны всегда добавлять к используемому ими URI
контента строку
{@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER}. Это позволяет предотвратить пометку таких строк поставщиком как «грязных».
В противном случае изменения, внесенные адаптером синхронизации, будут рассматриваться как локальные изменения,
которые подлежат отправке на сервер, даже если источником таких изменений был сам сервер.
</p>
</td>
</tr>
<tr>
<td>«1» — с момента последней синхронизации были внесены изменения, которые необходимо отразить и на сервере.</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.RawContacts}</td>
<td>{@link android.provider.ContactsContract.SyncColumns#VERSION}</td>
<td>Номер версии этой строки.</td>
<td>
Поставщик контактов автоматически увеличивает это значение при каждом
изменении в строке или в связанных с ним данных.
</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.Data}</td>
<td>{@link android.provider.ContactsContract.DataColumns#DATA_VERSION}</td>
<td>Номер версии этой строки.</td>
<td>
Поставщик контактов автоматически увеличивает это значение при каждом
изменении в строке данных.
</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.RawContacts}</td>
<td>{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID}</td>
<td>
Строковое значение, которое служит уникальным идентификатором данного необработанного контакта в аккаунте,
в котором он был создан.
</td>
<td>
Когда адаптер синхронизации создает новый необработанный контакт, в этом столбце следует указать
уникальный идентификатор этого необработанного контакта на сервере. Когда же приложение Android создает
новый необработанный контакт, то приложению следует оставить этот столбец пустым. Это служит сигналом для
адаптера синхронизации, чтобы создать новый необработанный контакт на сервере и
получить значение {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID}.
<p>
В частности, идентификатор источника должен быть <strong>уникальным</strong> для каждого типа аккаунта
и неизменным при синхронизации.
</p>
<ul>
<li>
Уникальный: у каждого необработанного контакта для аккаунта должен быть свой собственный идентификатор источника. Если
не применить это требование, у вас обязательно возникнут проблемы с приложением «Контакты».
Обратите внимание, что два необработанных контакта одного и того же <em>типа</em> аккаунта могут
иметь одинаковый идентификатор источника. Например, необработанный контакт Thomas Higginson для
аккаунта {@code emily.dickinson@gmail.com} может иметь такой же
идентификатор источника, что и контакт Thomas Higginson для аккаунта
{@code emilyd@gmail.com}.
</li>
<li>
Стабильный: идентификаторы источников представляют собой неизменную часть данных онлайн-службы для
необработанного контакта. Например, если пользователь выполняет повторную синхронизацию после очистки хранилища контактов в
настройках приложения, идентификаторы источников восстановленных необработанных контактов должны быть такими же,
как и прежде. Если не применить это требование, перестанут
работать ярлыки.
</li>
</ul>
</td>
</tr>
<tr>
<td rowspan="2">{@link android.provider.ContactsContract.Groups}</td>
<td rowspan="2">{@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE}</td>
<td>«0» — контакты, представленные в этой группе, не должны отображаться в интерфейсах пользователя приложений Android.</td>
<td>
Этот столбец предназначен для сведений о совместимости с серверами, позволяющими пользователям скрывать контакты
в определенных группах.
</td>
</tr>
<tr>
<td>«1» — контакты, представленные в этой группе, могут отображаться в интерфейсах пользователя приложений.</td>
</tr>
<tr>
<td rowspan="2">{@link android.provider.ContactsContract.Settings}</td>
<td rowspan="2">
{@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE}</td>
<td>
«0» — для этого аккаунта и аккаунтов этого типа контакты, не принадлежащие к группе,
не отображаются в интерфейсах пользователя приложений Android.
</td>
<td rowspan="2">
По умолчанию контакты скрыты, если ни один из их необработанных контактов не принадлежит группе
(принадлежность необработанного контакта к группе указывается в одном или нескольких строках
{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}
в таблице {@link android.provider.ContactsContract.Data}).
Установив этот флаг в строке таблицы {@link android.provider.ContactsContract.Settings}
для типа аккаунта и имени аккаунта, вы можете принудительно сделать видимыми контакты, не принадлежащие какой-либо группе.
Один из вариантов использования этого флага — отображение контактов с серверов, на которых не используются группы.
</td>
</tr>
<tr>
<td>
«1» — для этого аккаунта и аккаунтов этого типа контакты, не принадлежащие к группе,
отображаются в интерфейсах пользователя приложений Android.
</td>
</tr>
<tr>
<td>{@link android.provider.ContactsContract.SyncState}</td>
<td>(все)</td>
<td>
Эта таблица используется для хранения метаданных для вашего адаптера синхронизации.
</td>
<td>
С помощью этой таблицы вы можете на постоянной основе хранить на устройстве сведения о состоянии синхронизации и другие связанные с
синхронизацией данные.
</td>
</tr>
</table>
<h2 id="Access">Доступ к поставщику контактов</h2>
<p>
В этом разделе рассматриваются инструкции по получению доступа к данным из поставщика контактов, в частности,
следующие:
</p>
<ul>
<li>
запросы объектов;
</li>
<li>
пакетное изменение;
</li>
<li>
получение и изменение данных с помощью намерений;
</li>
<li>
целостность данных.
</li>
</ul>
<p>
Дополнительные сведения о внесении изменений с помощью адаптеров синхронизации представлены в разделе
<a href="#SyncAdapters">Адаптеры синхронизации поставщика контактов</a>.
</p>
<h3 id="Entities">Запрос объектов</h3>
<p>
Таблицы поставщика контактов имеют иерархическую структуру, поэтому зачастую полезно
извлекать строку и все связанные с ней ее дочерние строки. Например, для отображения
всей информации о пользователе вы, возможно, захотите извлечь все строки
{@link android.provider.ContactsContract.RawContacts} для одной строки
{@link android.provider.ContactsContract.Contacts} или все строки
{@link android.provider.ContactsContract.CommonDataKinds.Email} для одной строки
{@link android.provider.ContactsContract.RawContacts}. Чтобы упростить этот процесс, в поставщике контактов имеются
<strong>объекты</strong>, которые выступают в роли соединителей базы данных между
таблицами.
</p>
<p>
Объект представляет собой подобие таблицы, состоящей из выбранных столбцов родительской таблицы и ее дочерней таблицы.
При запросе объекта вы предоставляете проекцию и критерии поиска на основе доступных в
объекте столбцов. В результате вы получаете объект {@link android.database.Cursor}, в котором
содержится одна строка для каждой извлеченной строки дочерней таблицы. Например, если запросить объект
{@link android.provider.ContactsContract.Contacts.Entity} для имени контакта и все строки
{@link android.provider.ContactsContract.CommonDataKinds.Email} для всех необработанных контактов,
соответствующих этому имени, вы получите объект {@link android.database.Cursor}, содержащий по одной строке
для каждой строки {@link android.provider.ContactsContract.CommonDataKinds.Email}.
</p>
<p>
Использование объектов упрощает запросы. С помощью объекта можно извлечь сразу все данные для
контакта или необработанного контакта, вместо того, чтобы сначала делать запрос к родительской таблице для получения
идентификатора и последующего запроса к дочерней таблице с использованием полученного идентификатора. Кроме того, поставщик контактов обрабатывает запрос
объекта за одну транзакцию, что гарантирует внутреннюю согласованность полученных
данных.
</p>
<p class="note">
<strong>Примечание.</strong> Объект обычно содержит не все столбцы
родительской и дочерней таблиц. Если вы попытаетесь изменить имя столбца, который отсутствует в списке
констант имени столбца для объекта, вы получите {@link java.lang.Exception}.
</p>
<p>
Ниже представлен пример кода для получения всех строк необработанного контакта для контакта. Это фрагмент
более крупного приложения, в котором имеются две операции: «основная» и «для получения сведений». Основная операция
служит для получения списка строк контактов; при выборе пользователем контакта операция отправляет его идентификатор в операцию
для получения сведений. Операция для получения сведений использует объект {@link android.provider.ContactsContract.Contacts.Entity}
для отображения всех строк данных, полученных ото всех необработанных контактов, которые связаны с выбранным
контактом.
</p>
<p>
Вот фрагмент кода операции «для получения сведений»:
</p>
<pre>
...
/*
* Appends the entity path to the URI. In the case of the Contacts Provider, the
* expected URI is content://com.google.contacts/#/entity (# is the ID value).
*/
mContactUri = Uri.withAppendedPath(
mContactUri,
ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
// Initializes the loader identified by LOADER_ID.
getLoaderManager().initLoader(
LOADER_ID, // The identifier of the loader to initialize
null, // Arguments for the loader (in this case, none)
this); // The context of the activity
// Creates a new cursor adapter to attach to the list view
mCursorAdapter = new SimpleCursorAdapter(
this, // the context of the activity
R.layout.detail_list_item, // the view item containing the detail widgets
mCursor, // the backing cursor
mFromColumns, // the columns in the cursor that provide the data
mToViews, // the views in the view item that display the data
0); // flags
// Sets the ListView's backing adapter.
mRawContactList.setAdapter(mCursorAdapter);
...
&#64;Override
public Loader&lt;Cursor&gt; onCreateLoader(int id, Bundle args) {
/*
* Sets the columns to retrieve.
* RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
* DATA1 contains the first column in the data row (usually the most important one).
* MIMETYPE indicates the type of data in the data row.
*/
String[] projection =
{
ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
ContactsContract.Contacts.Entity.DATA1,
ContactsContract.Contacts.Entity.MIMETYPE
};
/*
* Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
* contact collated together.
*/
String sortOrder =
ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
" ASC";
/*
* Returns a new CursorLoader. The arguments are similar to
* ContentResolver.query(), except for the Context argument, which supplies the location of
* the ContentResolver to use.
*/
return new CursorLoader(
getApplicationContext(), // The activity's context
mContactUri, // The entity content URI for a single contact
projection, // The columns to retrieve
null, // Retrieve all the raw contacts and their data rows.
null, //
sortOrder); // Sort by the raw contact ID.
}
</pre>
<p>
По завершении загрузки {@link android.app.LoaderManager} выполняет обратный вызов метода
{@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D)
onLoadFinished()}. Одним их входящих аргументов для этого метода является
{@link android.database.Cursor} с результатом запроса. В своем приложении вы можете
получить данные из этого объекта {@link android.database.Cursor} для его отображения или дальнейшей работы с ним.
</p>
<h3 id="Transactions">Пакетное изменение</h3>
<p>
При каждой возможности данные в поставщике контактов следует вставлять, обновлять и удалять в
«пакетном режиме» путем создания {@link java.util.ArrayList} из объектов
{@link android.content.ContentProviderOperation} и вызова метода
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Поскольку
поставщик контактов выполняет все операции в методе
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} за одну
транзакцию, ваши изменения всегда будут находиться в рамках репозитория контактов и
всегда будут согласованными. Пакетное изменение также упрощает вставку необработанного контакта одновременно с его подробными
данными.
</p>
<p class="note">
<strong>Примечание.</strong> Чтобы изменить <em>отдельный</em> необработанный контакт, рекомендуется отправить намерение в
приложение для работы с контактами на устройстве вместо обработки изменения в вашем приложении.
Более подробно эта операция описана в разделе
<a href="#Intents">Получение и изменение данных с помощью намерений</a>.
</p>
<h4>Пределы</h4>
<p>
Пакетное изменение большого количества операций может заблокировать выполнение других процессов,
что может негативно сказаться на работе пользователя с приложением. Чтобы упорядочить все необходимые изменения
в рамках как можно меньшего количества отдельных списков и при этом предотвратить
блокирование работы системы, следует задать <strong>пределы</strong> для одной или нескольких операций.
Предел представляет собой объект {@link android.content.ContentProviderOperation}, в качестве значения параметра
{@link android.content.ContentProviderOperation#isYieldAllowed()} которого задано
<code>true</code>. При достижении поставщиком контактов предела он приостанавливает свою работу,
чтобы могли выполняться другие процессы, и закрывает текущую транзакцию. Когда поставщик запускается снова, он
выполняет следующую операцию в {@link java.util.ArrayList} и запускает
новую транзакцию.
</p>
<p>
Использование пределов позволяет за один вызов метода
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} выполнять более одной транзакции. По этой причине
вам следует задать предел для последней операции с набором связанных строк.
Например, необходимо задать предел для последней операции в наборе, которая
добавляет строки необработанного контакта и связанные с ним строки данных, или для последней операции с набором строк, связанных
с одним контактом.
</p>
<p>
Пределы также являются единицами атомарных операций. Все операции доступа в промежутке между двумя пределами завершаются либо
успехом, либо сбоем в рамках одной единицы. Если любой из пределов не задан, наименьшей
атомарной операцией считается весь пакет операций. Использование пределов позволяет предотвратить
снижение производительности системы и одновременно обеспечить выполнение набора
операций на атомарном уровне.
</p>
<h4>Изменение обратных ссылок</h4>
<p>
При вставке новой строки необработанного контакта и связанных с ним рядов данных в виде набора объектов
{@link android.content.ContentProviderOperation} вам приходится связывать строки данных
со строкой необработанного контакта путем вставки значения
{@code android.provider.BaseColumns#_ID} необработанного контакта в виде значения
{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Однако это значение
недоступно, когда вы создаете{@link android.content.ContentProviderOperation}
для строки данных, поскольку вы еще не применили
{@link android.content.ContentProviderOperation} для строки необработанного контакта. Чтобы обойти это ограничение,
в классе {@link android.content.ContentProviderOperation.Builder} предусмотрен метод
{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}.
С помощью этого метода можно вставлять или изменять столбец
с результатом предыдущей операции.
</p>
<p>
В методе {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}
предусмотрено два аргумента:
</p>
<dl>
<dt>
<code>key</code>
</dt>
<dd>
Ключ для пары «ключ-значение». Значением этого аргумента должно быть имя столбца
в таблице, которую вы изменяете.
</dd>
<dt>
<code>previousResult</code>
</dt>
<dd>
Индекс значения, начинающийся с «0», в массиве объектов
{@link android.content.ContentProviderResult} из метода
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. По мере
выполнения пакетных операций результат каждой операции сохраняется в
промежуточном массиве результатов. Значением <code>previousResult</code> является индекс
одного из этих результатов, который извлекается и хранится со значением
<code>key</code>. Благодаря этому можно вставить новую запись необработанного контакта и получить обратно его значение
{@code android.provider.BaseColumns#_ID}, а затем создать «обратную ссылку» на
значение при добавлении строки {@link android.provider.ContactsContract.Data}.
<p>
Целиком весь результат создается при первом вызове метода
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}.
Размер результата равен размеру {@link java.util.ArrayList} предоставленных вами объектов
{@link android.content.ContentProviderOperation}. Однако для всех
элементов в массиве результатов присваивается значение <code>null</code>, и при попытке
воспользоваться обратной ссылкой на результат еще не выполненной операции метод
{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}
выдает {@link java.lang.Exception}.
</p>
</dd>
</dl>
<p>
Ниже представлены фрагменты кода для вставки нового необработанного контакта и его данных в пакетном режиме. Они включают
код, который задает предел и использует обратную ссылку. Эти фрагменты
представляют собой расширенную версию метода <code>createContacEntry()</code>, который входит в класс
<code>ContactAdder</code> в примере приложения
<code><a href="{@docRoot}resources/samples/ContactManager/index.html">
Contact Manager</a></code>.
</p>
<p>
Первый фрагмент кода служит для извлечения данных контакта из пользовательского интерфейса. На этом этапе пользователь уже выбрал
аккаунт, для которого необходимо добавить новый необработанный контакт.
</p>
<pre>
// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
/*
* Gets values from the UI
*/
String name = mContactNameEditText.getText().toString();
String phone = mContactPhoneEditText.getText().toString();
String email = mContactEmailEditText.getText().toString();
int phoneType = mContactPhoneTypes.get(
mContactPhoneTypeSpinner.getSelectedItemPosition());
int emailType = mContactEmailTypes.get(
mContactEmailTypeSpinner.getSelectedItemPosition());
</pre>
<p>
В следующем фрагменте кода создается операция для вставки строки необработанного контакта в таблицу
{@link android.provider.ContactsContract.RawContacts}:
</p>
<pre>
/*
* Prepares the batch operation for inserting a new raw contact and its data. Even if
* the Contacts Provider does not have any data for this person, you can't add a Contact,
* only a raw contact. The Contacts Provider will then add a Contact automatically.
*/
// Creates a new array of ContentProviderOperation objects.
ArrayList&lt;ContentProviderOperation&gt; ops =
new ArrayList&lt;ContentProviderOperation&gt;();
/*
* Creates a new raw contact with its account type (server type) and account name
* (user's account). Remember that the display name is not stored in this row, but in a
* StructuredName data row. No other data is required.
*/
ContentProviderOperation.Builder op =
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
// Builds the operation and adds it to the array of operations
ops.add(op.build());
</pre>
<p>
Затем код создает строки данных для строк отображаемого имени, телефона и адреса эл. почты.
</p>
<p>
Каждый объект компонента операции использует метод
{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}
для получения
{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Ссылка возвращается
обратно к объекту {@link android.content.ContentProviderResult} из первой операции,
в результате чего добавляется строка необработанного контакта и возвращается его новое значение
{@code android.provider.BaseColumns#_ID}. После этого каждая строка данных автоматически связывается по своему
{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}
с новой строкой {@link android.provider.ContactsContract.RawContacts}, которой она принадлежит.
</p>
<p>
Объект {@link android.content.ContentProviderOperation.Builder}, который добавляет строку адреса эл. почты,
помечается флагом с помощью метода {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean)
withYieldAllowed()}, который задает предел:
</p>
<pre>
// Creates the display name for the new raw contact, as a StructuredName data row.
op =
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
/*
* withValueBackReference sets the value of the first argument to the value of
* the ContentProviderResult indexed by the second argument. In this particular
* call, the raw contact ID column of the StructuredName data row is set to the
* value of the result returned by the first operation, which is the one that
* actually adds the raw contact row.
*/
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
// Sets the data row's MIME type to StructuredName
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
// Sets the data row's display name to the name in the UI.
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
// Builds the operation and adds it to the array of operations
ops.add(op.build());
// Inserts the specified phone number and type as a Phone data row
op =
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
/*
* Sets the value of the raw contact id column to the new raw contact ID returned
* by the first operation in the batch.
*/
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
// Sets the data row's MIME type to Phone
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
// Sets the phone number and type
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);
// Builds the operation and adds it to the array of operations
ops.add(op.build());
// Inserts the specified email and type as a Phone data row
op =
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
/*
* Sets the value of the raw contact id column to the new raw contact ID returned
* by the first operation in the batch.
*/
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
// Sets the data row's MIME type to Email
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
// Sets the email address and type
.withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);
/*
* Demonstrates a yield point. At the end of this insert, the batch operation's thread
* will yield priority to other threads. Use after every set of operations that affect a
* single contact, to avoid degrading performance.
*/
op.withYieldAllowed(true);
// Builds the operation and adds it to the array of operations
ops.add(op.build());
</pre>
<p>
В последнем фрагменте кода представлен вызов метода
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}
для вставки нового необработанного контакта и его строк данных.
</p>
<pre>
// Ask the Contacts Provider to create a new contact
Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
mSelectedAccount.getType() + ")");
Log.d(TAG,"Creating contact: " + name);
/*
* Applies the array of ContentProviderOperation objects in batch. The results are
* discarded.
*/
try {
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
// Display a warning
Context ctx = getApplicationContext();
CharSequence txt = getString(R.string.contactCreationFailure);
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(ctx, txt, duration);
toast.show();
// Log exception
Log.e(TAG, "Exception encountered while inserting contact: " + e);
}
}
</pre>
<p>
С помощью пакетных операций можно также реализовать<strong>оптимистическое управление параллелизмом</strong>.
Это метод применения транзакций изменения без необходимости блокировать базовый репозиторий.
Чтобы воспользоваться этим методом, необходимо применить транзакцию и проверить наличие других изменений,
которые могли быть внесены в это же время. Если обнаружится, что имеется несогласованное изменение,
транзакцию можно откатить и выполнить повторно.
</p>
<p>
Оптимистическое управление параллелизмом подходит для мобильных устройств, где с системой одновременно взаимодействует только один пользователь,
а одновременный доступ к репозиторию данных нескольких пользователей довольно редкое явление. Поскольку не применяется блокировка,
экономится ценное время, которое требуется на установку блокировок или ожидания того, когда другие транзакции отменят свои блокировки.
</p>
<p>
Ниже представлен порядок использования оптимистического управления параллелизмом при обновлении одной строки
{@link android.provider.ContactsContract.RawContacts}.
</p>
<ol>
<li>
Извлеките столбец {@link android.provider.ContactsContract.SyncColumns#VERSION}
необработанного контакта, а также другие данные, которые необходимо извлечь.
</li>
<li>
Создайте объект {@link android.content.ContentProviderOperation.Builder}, подходящий для
применения ограничения, с помощью метода
{@link android.content.ContentProviderOperation#newAssertQuery(Uri)}. Для URI контента
используйте {@link android.provider.ContactsContract.RawContacts#CONTENT_URI
RawContacts.CONTENT_URI},
добавив к нему {@code android.provider.BaseColumns#_ID} необработанного контакта.
</li>
<li>
Для объекта {@link android.content.ContentProviderOperation.Builder} вызовите метод
{@link android.content.ContentProviderOperation.Builder#withValue(String, Object)
withValue()}, чтобы сравнить столбец {@link android.provider.ContactsContract.SyncColumns#VERSION}
с номером версии, которую вы только что получили.
</li>
<li>
Для того же объекта {@link android.content.ContentProviderOperation.Builder} вызовите метод
{@link android.content.ContentProviderOperation.Builder#withExpectedCount(int)
withExpectedCount()}, чтобы убедиться в том, что проверочное утверждение проверяет только одну строка.
</li>
<li>
Вызовите метод {@link android.content.ContentProviderOperation.Builder#build()}, чтобы создать объект
{@link android.content.ContentProviderOperation}, затем добавьте этот объект в качестве первого объекта в
{@link java.util.ArrayList}, который вы передаете в метод
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}.
</li>
<li>
Примените пакетную транзакцию.
</li>
</ol>
<p>
Если в промежутке между считыванием строки и попыткой ее изменения строка необработанного контакта была обновлена другой
операцией, assert {@link android.content.ContentProviderOperation}
завершится сбоем и в выполнении всего пакета операций будет отказано. Можно выбрать повторное выполнение
пакета или выполнить другое действие.
</p>
<p>
В примере кода ниже демонстрируется, как создать assert
{@link android.content.ContentProviderOperation} после запроса одного необработанного контакта с помощью
{@link android.content.CursorLoader}.
</p>
<pre>
/*
* The application uses CursorLoader to query the raw contacts table. The system calls this method
* when the load is finished.
*/
public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
// Gets the raw contact's _ID and VERSION values
mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}
...
// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);
// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList&lt;ContentProviderOperationg&gt;;
ops.add(assertOp.build());
// You would add the rest of your batch operations to "ops" here
...
// Applies the batch. If the assert fails, an Exception is thrown
try
{
ContentProviderResult[] results =
getContentResolver().applyBatch(AUTHORITY, ops);
} catch (OperationApplicationException e) {
// Actions you want to take if the assert operation fails go here
}
</pre>
<h3 id="Intents">Получение и изменение данных с помощью намерений</h3>
<p>
С помощью отправки намерения в приложение для работы с контактами, которое имеется на устройстве, можно в обход получить доступ
к поставщику контактов. Намерение запускает пользовательский интерфейс приложения на устройстве, посредством которого пользователь
может работать с контактами. Такой тип доступа позволяет пользователю выполнять следующие действия:
<ul>
<li>выбор контактов из списка и их возврат в приложение для дальнейшей работы;</li>
<li>изменение данных существующего контакта;</li>
<li>вставка нового необработанного контакта для любых других аккаунтов;</li>
<li>удаление контакта или его данных.</li>
</ul>
<p>
Если пользователь вставляет или обновляет данные , вы можете сначала собрать эти данные, а затем отправить их вместе
с намерением.
</p>
<p>
При использовании намерений для доступа к поставщику контактов посредством приложения для работы с контактами, имеющегося на устройстве, вам
не нужно создавать собственный пользовательский интерфейс или код для доступа к поставщику. Также вам
не нужно запрашивать разрешение на чтение или запись в поставщик. Приложение для работы с контактами, имеющееся на устройстве, может
делегировать вам разрешение на чтение контакта, и поскольку вы вносите изменения
в поставщик через другое приложение, вам не нужно разрешение на запись.
</p>
<p>
Более подробно общий процесс отправки намерения для получения доступа к поставщику описан в руководстве
<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">Основные сведения о поставщике контента</a>
в разделе «Доступ к данным посредством намерений». В таблице 4 ниже представлены сводные сведения об операциях,
типе MIME и значениях данных, которые используются для доступных задач. Значения
дополнительных данных, которые можно использовать для
{@link android.content.Intent#putExtra(String, String) putExtra()}, указаны в справочной
документации по {@link android.provider.ContactsContract.Intents.Insert}.
</p>
<p class="table-caption" id="table4">
<strong>Таблица 4.</strong> Намерения в поставщике контактов.
</p>
<table style="width:75%">
<tr>
<th scope="col" style="width:10%">Задача</th>
<th scope="col" style="width:5%">Действие</th>
<th scope="col" style="width:10%">Данные</th>
<th scope="col" style="width:10%">Тип MIME</th>
<th scope="col" style="width:25%">Примечания</th>
</tr>
<tr>
<td><strong>Выбор контакта из списка</strong></td>
<td>{@link android.content.Intent#ACTION_PICK}</td>
<td>
Одно из следующего:
<ul>
<li>
{@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}
(отображение списка контактов);
</li>
<li>
{@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI}
(отображение номеров телефонов для необработанного контакта);
</li>
<li>
{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI
StructuredPostal.CONTENT_URI}
(отображение списка почтовых адресов для необработанного контакта);
</li>
<li>
{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI}
(отображение адресов эл. почты для необработанного контакта).
</li>
</ul>
</td>
<td>
Не используется
</td>
<td>
Отображение списка необработанных контактов или списка данных необработанного контакта зависимости от
предоставленного URI контента).
<p>
Вызовите метод
{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()},
который возвращает URI контента для выбранной строки. URI представлен в форме
URI контента таблицы, к которому добавлен <code>LOOKUP_ID</code> строки.
Приложение для работы с контактами, установленное на устройстве, делегирует этому URI контента
разрешения на чтение и запись, которые действуют в течение всего жизненного цикла вашей операции. Дополнительные сведения представлены в статье
<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">Основные
сведения о поставщике контента</a>.
</p>
</td>
</tr>
<tr>
<td><strong>Вставка нового необработанного контакта</strong></td>
<td>{@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION}</td>
<td>Н/Д</td>
<td>
{@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE
RawContacts.CONTENT_TYPE}, тип MIME для набора необработанных контактов.
</td>
<td>
Отображение экрана <strong>Добавить контакт</strong> в приложении для работы с контактами, которое установлено на устройстве. Отображаются
значения дополнительных данных, добавленных вами в намерение. При отправке с помощью метода
{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}
URI контента нового добавленного необработанного контакта передается обратно в метод обратного вызова
{@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()}
вашей операции в аргументе {@link android.content.Intent} в поле
data. Чтобы получить значение, вызовите метод {@link android.content.Intent#getData()}.
</td>
</tr>
<tr>
<td><strong>Изменение контакта</strong></td>
<td>{@link android.content.Intent#ACTION_EDIT}</td>
<td>
{@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}
контакта. Операция редактора разрешит пользователю изменить любые данные, связанные с
этим контактом.
</td>
<td>
{@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE
Contacts.CONTENT_ITEM_TYPE}, один контакт.</td>
<td>
Отображение экрана редактирования контакта в приложении для работы с контактами. Отображаются
значения дополнительных данных, добавленных вами в намерение. Когда пользователь нажимает на кнопку <strong>Готово</strong> для сохранения
внесенных изменений, ваша операция возвращается на передний план.
</td>
</tr>
<tr>
<td><strong>Отображение средства выбора, в котором также можно добавлять данные</strong></td>
<td>{@link android.content.Intent#ACTION_INSERT_OR_EDIT}</td>
<td>
Н/Д
</td>
<td>
{@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE}
</td>
<td>
Это намерение также отображает экран средства выбора приложения для работы с контактами. Пользователь
может выбрать контакт для изменения или добавить новый контакт. В зависимости от выбора отображается экран редактирования или добавления контакта,
и отображаются дополнительные данные,
переданные вами в намерение. Если ваше приложение отображает такие данные контакта, как адрес эл. почты или номер телефона,
воспользуйтесь этим намерением, чтобы разрешить пользователю добавлять данные к существующему
контакту.
<p class="note">
<strong>Примечание.</strong> Вам не нужно отправлять значение имени в дополнительные данные этого намерения,
поскольку пользователь всегда выбирает существующее имя или добавляет новое. Кроме того,
если отправить имя, а пользователь решит внести изменения, приложение для работы с контактами
отобразит отправленное вами имя, перезаписав предыдущее значение. Если пользователь
не заметит этого и сохранит изменения, старое значение будет утеряно.
</p>
</td>
</tr>
</table>
<p>
Приложение для работы с контактами, имеющееся на устройстве, не разрешает вам удалять необработанные контакты или любые его данные с
помощью намерений. Вместо этого для обработки необработанного контакта используйте метод
{@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()}
или {@link android.content.ContentProviderOperation#newDelete(Uri)
ContentProviderOperation.newDelete()}.
</p>
<p>
Ниже представлен фрагмент кода для создания и отправки намерения, который вставляет новый
необработанный контакт и его данные.
</p>
<pre>
// Gets values from the UI
String name = mContactNameEditText.getText().toString();
String phone = mContactPhoneEditText.getText().toString();
String email = mContactEmailEditText.getText().toString();
String company = mCompanyName.getText().toString();
String jobtitle = mJobTitle.getText().toString();
// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);
// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);
/*
* Demonstrates adding data rows as an array list associated with the DATA key
*/
// Defines an array list to contain the ContentValues objects for each row
ArrayList&lt;ContentValues&gt; contactData = new ArrayList&lt;ContentValues&gt;();
/*
* Defines the raw contact row
*/
// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();
// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
// Adds the row to the array
contactData.add(rawContactRow);
/*
* Sets up the phone number data row
*/
// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();
// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);
// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
// Adds the row to the array
contactData.add(phoneRow);
/*
* Sets up the email data row
*/
// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();
// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);
// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
// Adds the row to the array
contactData.add(emailRow);
/*
* Adds the array to the intent's extras. It must be a parcelable object in order to
* travel between processes. The device's contacts app expects its key to be
* Intents.Insert.DATA
*/
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);
// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);
</pre>
<h3 id="DataIntegrity">Целостность данных</h3>
<p>
Поскольку в репозитории контактов содержится важная и конфиденциальная информация, которая, как ожидает пользователь, верна и
актуальна, в поставщике контактов предусмотрены четко определенные правила для обеспечения целостности данных. При изменении данных контактов вы обязаны
соблюдать эти правила. Вот наиболее
важные из этих правил:
</p>
<dl>
<dt>
Всегда добавляйте строку {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}
к каждой добавляемой вами строке {@link android.provider.ContactsContract.RawContacts}.
</dt>
<dd>
Если в таблице {@link android.provider.ContactsContract.Data} имеется строка {@link android.provider.ContactsContract.RawContacts}
без строки {@link android.provider.ContactsContract.CommonDataKinds.StructuredName},
то могут возникнуть
проблемы во время агрегирования.
</dd>
<dt>
Всегда связывайте новые строки {@link android.provider.ContactsContract.Data} с их
родительскими строками {@link android.provider.ContactsContract.RawContacts}.
</dt>
<dd>
Строка {@link android.provider.ContactsContract.Data}, которая не связана
с {@link android.provider.ContactsContract.RawContacts}, не будет отображаться в
приложении для работы с контактами, имеющемся на устройстве, а также может привести к проблемам с адаптерами синхронизации.
</dd>
<dt>
Следует изменять данные только тех необработанных контактов, которыми вы владеете.
</dt>
<dd>
Помните о том, что поставщик контактов обычно управляет данными из
нескольких аккаунтов различных типов или служб в Интернете. Необходимо убедиться в том, что ваше приложение изменяет
или удаляет данные только для тех строк, которые принадлежат вам, а также в том, что оно вставляет данные с использованием только тех типов и имени аккаунта,
которыми вы управляете.
</dd>
<dt>
Всегда используйте
для центров, URI контента, путей URI, имен столбцов, типов MIME и значений
{@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE} только те константы, которые определены в классе {@link android.provider.ContactsContract} и его подклассах.
</dt>
<dd>
Это позволит избежать ошибок. Если какая либо константа устарела, компилятор сообщит об этом
с помощью соответствующего предупреждения.
</dd>
</dl>
<h3 id="CustomData">Настраиваемые строки данных</h3>
<p>
Создав и используя собственные типы MIME, вы можете вставлять, изменять, удалять и извлекать
в таблице{@link android.provider.ContactsContract.Data} собственные строки данных. Ваши строки
ограничены использованием столбца, который определен в
{@link android.provider.ContactsContract.DataColumns}, однако вы можете сопоставить ваши собственные имена столбцов по типам строк
с именами столбцов по умолчанию. Данные для ваших строк отображаются в приложении для работы с контактами, которое имеется на устройстве,
однако их не удастся изменить или удалить, а также пользователи не смогут
добавить дополнительные данные. Чтобы разрешить пользователям изменять ваши настраиваемые строки данных, необходимо реализовать в вашем приложении операцию
редактора.
</p>
<p>
Для отображения настраиваемых данных укажите файл <code>contacts.xml</code>, содержащий элемент
<code>&lt;ContactsAccountType&gt;</code> и один или несколько его
<code>&lt;ContactsDataKind&gt;</code> дочерних элементов. Дополнительные сведения об этом представлены в разделе
<a href="#SocialStreamDataKind"><code>&lt;ContactsDataKind&gt; element</code></a>.
</p>
<p>
Дополнительные сведения о настраиваемых типах MIME представлены в руководстве
<a href="{@docRoot}guide/topics/providers/content-provider-creating.html">Создание
поставщика контента</a>.
</p>
<h2 id="SyncAdapters">Адаптеры синхронизации поставщика контактов</h2>
<p>
Поставщик контактов разработан специально для обработки операций <strong>синхронизации</strong>
данных контактов между устройством и службой в Интернете. Благодаря этому пользователи
могут загружать существующие данные на новое устройство и отправлять их в новый аккаунт.
Синхронизация также обеспечивает предоставление пользователю всегда актуальных сведений, независимо от
источника их добавления и внесенных в них изменений. Еще одно преимущество синхронизации заключается в том,
что данные контактов доступны даже в том случае, если устройство не подключено к сети.
</p>
<p>
Несмотря на множество различных способов реализации синхронизации данных, в системе Android имеется
подключаемая платформа синхронизации, которая позволяет автоматизировать выполнение следующих задач:
<ul>
<li>
проверка доступности сети;
</li>
<li>
планирование и выполнение синхронизации на основе предпочтений пользователя;
</li>
<li>
перезапуск остановленных процессов синхронизации.
</li>
</ul>
<p>
Для использования этой платформы вы предоставляете подключаемый модуль адаптера синхронизации. Каждый адаптер синхронизации является уникальным
для службы и поставщика контента, однако способен работать с несколькими аккаунтами в одной службе. В платформе
также предусмотрена возможность использовать несколько адаптеров синхронизации для одной и той же службы или поставщика.
</p>
<h3 id="SyncClassesFiles">Классы и файлы адаптера синхронизации</h3>
<p>
Адаптер синхронизации реализуется как подкласс класса
{@link android.content.AbstractThreadedSyncAdapter} и устанавливается в составе приложения
Android. Система узнает о наличии адаптера синхронизации из элементов в манифесте
вашего приложения, а также из особого файла XML, на который имеется указание в манифесте. В файле XML определяются
тип аккаунта в онлайн-службе и центр поставщика контента, которые вместе служат уникальными
идентификаторами адаптера. Адаптер синхронизации находится в неактивном состоянии до тех пор, пока пользователь не добавит
тип аккаунта и не включит синхронизацию
с поставщиком контента. На этом этапе система вступает в управление адаптером
, при необходимости вызывая его для синхронизации данных между поставщиком контента и сервером.
</p>
<p class="note">
<strong>Примечание.</strong> Использование типа аккаунта для идентификации адаптера синхронизации
позволяет системе обнаруживать и группировать адаптеры синхронизации, которые обращаются к разным службам
из одной и той же организации. Например, у всех адаптеров синхронизации для онлайн-служб Google
один и тот же тип аккаунта — <code>com.google</code>. Когда пользователи добавляют на свои устройства аккаунт Google, все
установленные адаптеры синхронизации группируются вместе; каждый из адаптеров
синхронизируется только с отдельным поставщиком контента на устройстве.
</p>
<p>
Поскольку в большинстве служб пользователям сначала необходимо подтвердить свою подлинность, прежде чем они смогут получить доступ к данным,
система Android предлагает платформу аутентификации, которая аналогична
платформе адаптера синхронизации и зачастую используется совместно с ней. Платформа аутентификации
использует подключаемые структуры проверки подлинности, которые представляют собой подклассы класса
{@link android.accounts.AbstractAccountAuthenticator}. Такая структура проверяет
подлинность пользователя следующим образом:
<ol>
<li>
Сначала собираются сведения об имени пользователя и его пароле или аналогичная информация (<strong>учетные данные</strong>
пользователя).
</li>
<li>
Затем учетные данные оправляются в службу.
</li>
<li>
Наконец, изучается ответ, полученный от службы.
</li>
</ol>
<p>
Если служба приняла учетные данные, структура проверки подлинности может
сохранить их для использования в дальнейшем. Благодаря использованию структур проверки подлинности
{@link android.accounts.AccountManager} может предоставить доступ к любым маркерам аутентификации, которые
поддерживает структура проверки подлинности и которые она решает предоставить, например, к маркерам аутентификации OAuth2.
</p>
<p>
Несмотря на то что аутентификация не требуется, она используется большинством служб для работы с контактами.
Тем не менее, вам не обязательно использовать платформу аутентификации Android для проверки подлинности.
</p>
<h3 id="SyncAdapterImplementing">Реализация адаптера синхронизации</h3>
<p>
Чтобы реализовать адаптер синхронизации для поставщика контактов, начните с создания
приложения Android, которое содержит следующие компоненты.
</p>
<dl>
<dt>
Компонент {@link android.app.Service}, который отвечает на запросы системы
для привязки к адаптеру синхронизации.
</dt>
<dd>
Когда системе требуется запустить синхронизацию, она вызывает метод
{@link android.app.Service#onBind(Intent) onBind()} службы, чтобы получить объект
{@link android.os.IBinder} для адаптера синхронизации. Благодаря этому система
может вызывать методы адаптера между процессами.
<p>
В
<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">примере адаптера синхронизации</a> именем класса этой службы является
<code>com.example.android.samplesync.syncadapter.SyncService</code>.
</p>
</dd>
<dt>
Адаптер синхронизации, фактически реализованный как конкретный подкласс
класса {@link android.content.AbstractThreadedSyncAdapter}.
</dt>
<dd>
Этот класс не подходит для загрузки данных с сервера, отправки данных
с устройства и разрешения конфликтов. Основную свою работу адаптер
выполняет в методе {@link android.content.AbstractThreadedSyncAdapter#onPerformSync(
Account, Bundle, String, ContentProviderClient, SyncResult)
onPerformSync()}. Этот класс допускает создание только одного экземпляра.
<p>
В
<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">примере адаптера синхронизации</a> адаптер определяется в классе
<code>com.example.android.samplesync.syncadapter.SyncAdapter</code>.
</p>
</dd>
<dt>
Подкласс класса {@link android.app.Application}.
</dt>
<dd>
Этот класс выступает в роли фабрики для единственного экземпляра адаптера синхронизации. Воспользуйтесь методом
{@link android.app.Application#onCreate()}, чтобы создать экземпляр адаптера синхронизации, а затем
предоставьте статический метод get, чтобы возвратить единственный экземпляр в метод
{@link android.app.Service#onBind(Intent) onBind()} службы
адаптера синхронизации.
</dd>
<dt>
<strong>Необязательно:</strong> компонент {@link android.app.Service}, который отвечает на запросы
системы для аутентификации пользователей.
</dt>
<dd>
{@link android.accounts.AccountManager} запускает службу, чтобы начать процесс
аутентификации. Метод {@link android.app.Service#onCreate()} службы создает экземпляр
объекта структуры проверки подлинности. Когда системе требуется запустить аутентификацию аккаунта пользователя для
адаптера синхронизации приложения, она вызывает метод
{@link android.app.Service#onBind(Intent) onBind()} службы, чтобы получить объект
{@link android.os.IBinder} для структуры проверки подлинности. Благодаря этому система
может вызывать методы структуры проверки подлинности между процессами.
<p>
В
<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">примере адаптера синхронизации</a> именем класса этой службы является
<code>com.example.android.samplesync.authenticator.AuthenticationService</code>.
</p>
</dd>
<dt>
<strong>Необязательно:</strong> конкретный подкласс класса
{@link android.accounts.AbstractAccountAuthenticator}, который обрабатывает запросы на
аутентификацию.
</dt>
<dd>
В этом классе имеются методы, которые {@link android.accounts.AccountManager} вызывает
для проверки подлинности учетных данных пользователя на сервере. Подробности процесса
аутентификации значительно различаются в зависимости от технологии, используемой на сервере. Чтобы узнать подробнее об аутентификации,
обратитесь к соответствующей документации к программному обеспечению используемого сервера.
<p>
В
<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">примере адаптера синхронизации</a> структура проверки подлинности определяется в классе
<code>com.example.android.samplesync.authenticator.Authenticator</code>.
</p>
</dd>
<dt>
Файлы XML, в которых определяются адаптер синхронизация и структура проверки подлинности для системы.
</dt>
<dd>
Описанные ранее компоненты службы адаптера синхронизации и структуры проверки подлинности определяются
в элементах
<code>&lt;<a href="{@docRoot}guide/topics/manifest/service-element.html">service</a>&gt;</code>
в манифесте приложения. Эти элементы
включают дочерние элементы
<code>&lt;<a href="{@docRoot}guide/topics/manifest/meta-data-element.html">meta-data</a>&gt;</code>
, в которых имеются определенные данные для
системы.
<ul>
<li>
Элемент
<code>&lt;<a href="{@docRoot}guide/topics/manifest/meta-data-element.html">meta-data</a>&gt;</code>
для службы адаптера синхронизации указывает на файл
XML <code>res/xml/syncadapter.xml</code>. В свою очередь, в этом файле задается
URI веб-службы для синхронизации с поставщиком контактов,
а также тип аккаунта для этой веб-службы.
</li>
<li>
<strong>Необязательно:</strong> элемент
<code>&lt;<a href="{@docRoot}guide/topics/manifest/meta-data-element.html">meta-data</a>&gt;</code>
для структуры проверки подлинности указывает на файл XML
<code>res/xml/authenticator.xml</code>. В свою очередь, в этом файле задается
тип аккаунта, который поддерживает структура проверки подлинности, а также ресурсы пользовательского интерфейса,
которые отображаются в процессе аутентификации. Тип аккаунта, указанный в этом
элементе, должен совпадать с типом аккаунта, который задан для
адаптера синхронизации.
</li>
</ul>
</dd>
</dl>
<h2 id="SocialStream">Потоки данных из социальных сетей</h2>
<p>
Для управления входящими данными из социальных сетей используются таблицы {@code android.provider.ContactsContract.StreamItems}
и
{@code android.provider.ContactsContract.StreamItemPhotos}. Можно создать адаптер синхронизации, который добавляет поток данных
из вашей собственной сети в эти таблицы, либо вы можете считывать поток данных из этих таблиц и отображать
их в собственном приложении. Можно также реализовать оба этих способа. С помощью этих функций вы можете интегрировать службы социальных сетей
в компоненты Android для работы с социальными сетями.
</p>
<h3 id="StreamText">Текст из потока данных из социальных сетей</h3>
<p>
Элементы потока всегда ассоциируются с необработанным контактом. Идентификатор
{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} связывается со значением
<code>_ID</code> необработанного контакта. Тип аккаунта и его имя для необработанного
контакта также хранятся в строке элемента потока.
</p>
<p>
Данные из потока следует хранить в следующих столбцах:
</p>
<dl>
<dt>
{@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE}
</dt>
<dd>
<strong>Обязательный.</strong> Тип аккаунта пользователя для необработанного контакта, связанного с
этим элементом потока. Не забудьте задать это значение при вставке элемента потока.
</dd>
<dt>
{@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME}
</dt>
<dd>
<strong>Обязательный.</strong> Имя аккаунта пользователя для необработанного контакта, связанного с
этим элементом потока. Не забудьте задать это значение при вставке элемента потока.
</dd>
<dt>
Столбцы идентификатора
</dt>
<dd>
<strong>Обязательный.</strong> При вставке элемента потока необходимо вставить
указанные ниже столбцы идентификатора.
<ul>
<li>
{@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID}: значение
{@code android.provider.BaseColumns#_ID} для контакта, с которым ассоциирован
этот элемент потока.
</li>
<li>
{@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY}: значение
{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} для контакта, с которым ассоциирован
этот элемент потока.
</li>
<li>
{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID}: значение
{@code android.provider.BaseColumns#_ID} для необработанного контакта, с которым ассоциирован
этот элемент потока.
</li>
</ul>
</dd>
<dt>
{@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS}
</dt>
<dd>
Необязательный. В нем хранится сводная информация, которую можно отобразить в начале элемента потока.
</dd>
<dt>
{@code android.provider.ContactsContract.StreamItemsColumns#TEXT}
</dt>
<dd>
Текст элемента потока: либо контент, опубликованный источником элемента,
либо описание некоторого действия, сгенерировавшего элемент потока. В этом столбце могут содержаться
любое форматирование и встроенные изображения ресурсов, рендеринг которых можно выполнить с помощью метода
{@link android.text.Html#fromHtml(String) fromHtml()}. Поставщик может обрезать слишком длинный контент
или заменить его часть многоточием, однако он попытается избежать нарушения тегов.
</dd>
<dt>
{@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP}
</dt>
<dd>
Текстовая строка с информацией о времени вставки или обновления элемента в
<em>миллисекундах</em> от начала отсчета времени. Обслуживанием этого столбца занимаются приложения, которые вставляют или
обновляют элементы потока; поставщик контактов не выполняет
это автоматически.
</dd>
</dl>
<p>
Для отображения идентификационной информации для элементов потока воспользуйтесь
{@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON},
{@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL} и
{@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} для связывания с ресурсами
в вашем приложении.
</p>
<p>
В таблице {@code android.provider.ContactsContract.StreamItems} также имеются столбцы
{@code android.provider.ContactsContract.StreamItemsColumns#SYNC1}—{@code android.provider.ContactsContract.StreamItemsColumns#SYNC4},
которые предназначены исключительно для
адаптеров синхронизации.
</p>
<h3 id="StreamPhotos">Фотографии из потока данных из социальных сетей</h3>
<p>
Фотографии, связанные с элементом потока, хранятся в таблице
{@code android.provider.ContactsContract.StreamItemPhotos}. Столбец
{@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID}
в этой таблице связан со столбцом {@code android.provider.BaseColumns#_ID}
в таблице {@code android.provider.ContactsContract.StreamItems}. Ссылки на фотографии хранятся в следующих столбцах
таблицы:
</p>
<dl>
<dt>
Столбец {@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} (объект BLOB).
</dt>
<dd>
Представление фотографии в двоичном формате и с измененным поставщиком размером для ее хранения и отображения.
Этот столбец доступен для обеспечения обратной совместимости с предыдущими версиями поставщика
контактов, которые использовались для хранения фотографий. Однако в текущей версии
поставщика мы не рекомендуем использовать этот столбец для хранения фотографий. Вместо этого воспользуйтесь столбцом
{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} или
{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} (или обоими
столбцами, как описано далее) для хранения фотографий в файле. В этом
столбце теперь хранятся миниатюры фотографий, доступных для чтения.
</dd>
<dt>
{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID}
</dt>
<dd>
Числовой идентификатор фотографии для необработанного контакта. Добавьте это значение к константе
{@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI},
чтобы получить URI контента для одного файла фотографии, а затем вызовите метод
{@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)
openAssetFileDescriptor()}, чтобы получить средство обработки файла фотографии.
</dd>
<dt>
{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI}
</dt>
<dd>
URI контента, указывающий на файл фотографии, для фотографии, которая представлена этой строкой.
Вызовите метод {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)
openAssetFileDescriptor()}, передав в него этот URI, чтобы получить средство обработки файла фотографии.
</dd>
</dl>
<h3 id="SocialStreamTables">Использование таблиц из потока данных из социальных сетей</h3>
<p>
Эти таблицы работают аналогично другим основным таблицам в поставщике контактов, за исключением указанных ниже моментов.
</p>
<ul>
<li>
Для работы с этими таблицами требуются дополнительные разрешения на доступ. Для чтения данных из них вашему приложению
должно быть предоставлено разрешение {@code android.Manifest.permission#READ_SOCIAL_STREAM}. Для
изменения таблиц ваше приложение должно иметь разрешение
{@code android.Manifest.permission#WRITE_SOCIAL_STREAM}.
</li>
<li>
Для таблицы {@code android.provider.ContactsContract.StreamItems} существует ограничение на количество строк,
которое можно хранить для каждого необработанного контакта. При достижении этого ограничения
поставщик контактов освобождает место для новых строк элементов потока путем автоматического удаления
строк со самой старой меткой
{@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP}. Чтобы получить это ограничение,
запросите URI контента
{@code android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI}. Для всех аргументов,
отличных от URI контента, можно оставить значение <code>null</code>. Запрос возвращает
объект Cursor, в котором содержится одна строка с одним столбцом
{@code android.provider.ContactsContract.StreamItems#MAX_ITEMS}.
</li>
</ul>
<p>
Класс {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} определяет
дочернюю таблицу объектов {@code android.provider.ContactsContract.StreamItemPhotos}, в которой содержатся
строки для одного элемента потока.
</p>
<h3 id="SocialStreamInteraction">Взаимодействие с потоками данных из социальных сетей</h3>
<p>
Управление потоком данных из социальных сетей осуществляется поставщиком контактов совместно с приложением для управления контактами, имеющимся на устройстве.
Такой подход позволяет организовать эффективное использование данных из социальных сетей
с данными о существующих контактах. Доступны указанные ниже функции.
</p>
<ul>
<li>
Организовав синхронизацию данных из социальной службы с поставщиком контактов посредством
адаптера синхронизации, вы можете получать данные о недавней активности контактов пользователя и хранить такие данные в таблицах
,{@code android.provider.ContactsContract.StreamItems}
и {@code android.provider.ContactsContract.StreamItemPhotos} для использования в дальнейшем.
</li>
<li>
Помимо регулярной синхронизации, адаптер синхронизации можно настроить на получение
дополнительных данных при выборе пользователем контакта для просмотра. Благодаря этому ваш адаптер синхронизации
может получать фотографии высокого разрешения и самые актуальные элементы потока для контакта.
</li>
<li>
Регистрируя уведомление в приложении для работы с контактами и в поставщике
контактов, вы можете<em>получать</em> намерения при просмотре контакта и обновлять на этом этапе
данные о состоянии контакта из вашей службы. Такой подход может обеспечить большее быстродействие и меньший объем
использования полосы пропускания, чем выполнение полной синхронизации с помощью адаптера синхронизации.
</li>
<li>
Пользователи могут добавить контакт в вашу службу социальной сети, обратившись к контакту
в приложении для работы с контактами, которое имеется на устройстве. Это реализуется с помощью функции «пригласить контакт»,
для включения которой используется сочетание операции,
которая добавляет существующий контакт в вашу сеть, и файла XML, в котором представлены сведения о вашем приложении для поставщика контактов и приложения для работы с
контактами.
</li>
</ul>
<p>
Регулярная синхронизация элементов потока с помощью поставщика контактов выполняется так же,
как и любая другая синхронизация. Дополнительные сведения о синхронизации представлены в разделе
<a href="#SyncAdapters">Адаптеры синхронизации поставщика контактов</a>. Регистрация уведомлений
и приглашение контактов рассматриваются в следующих двух разделах.
</p>
<h4>Регистрация для обработки просмотров контактов в социальных сетях</h4>
<p>
Чтобы зарегистрировать адаптер синхронизации для получения уведомлений о просмотрах пользователями контакта,
управление которым осуществляется вашим адаптером синхронизации, выполните указанные ниже действия.
</p>
<ol>
<li>
В каталоге <code>res/xml/</code> своего проекта создайте файл
<code>contacts.xml</code>. Если у вас уже есть этот файл, переходите к следующему действию.
</li>
<li>
В этом файле добавьте элемент
<code>&lt;ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"&gt;</code>.
Если этот элемент уже существует, можете переходить к следующему действию.
</li>
<li>
Чтобы зарегистрировать службу, которой отправляется уведомление при открытии пользователем страницы со сведениями о контакте
в приложении для работы с контактами, которое имеется на устройстве, добавьте атрибут
<code>viewContactNotifyService="<em>serviceclass</em>"</code> к элементу, где
<code><em>serviceclass</em></code> — это полное имя класса службы,
которая должна получить намерение из приложения для работы с контактами. Для службы-уведомителя
используйте класс, который является расширением класса {@link android.app.IntentService}, чтобы разрешить службе
получать намерения. Данные во входящем намерении содержат URI контента необработанного
контакта, выбранного пользователем. В службе-уведомителе можно привязать адаптер синхронизации, а затем вызвать его
для обновления данных для необработанного контакта.
</li>
</ol>
<p>
Чтобы зарегистрировать операцию, которую следует вызвать при выборе пользователем элемента потока или фотографии (или обоих элементов), выполните указанные ниже действия.
</p>
<ol>
<li>
В каталоге <code>res/xml/</code> своего проекта создайте файл
<code>contacts.xml</code>. Если у вас уже есть этот файл, переходите к следующему действию.
</li>
<li>
В этом файле добавьте элемент
<code>&lt;ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"&gt;</code>.
Если этот элемент уже существует, можете переходить к следующему действию.
</li>
<li>
Чтобы зарегистрировать одну из ваших операций для обработки выбора пользователем элемента потока
в приложении для работы с контактами, которое имеется на устройстве, добавьте атрибут
<code>viewStreamItemActivity="<em>activityclass</em>"</code> к элементу, где
<code><em>activityclass</em></code> — это полное имя класса операции,
которая должна получить намерение из приложения для работы с контактами.
</li>
<li>
Чтобы зарегистрировать одну из ваших операций для обработки выбора пользователем фотографии в потоке
в приложении для работы с контактами, которое имеется на устройстве, добавьте атрибут
<code>viewStreamItemPhotoActivity="<em>activityclass</em>"</code> к элементу, где
<code><em>activityclass</em></code> — это полное имя класса операции,
которая должна получить намерение из приложения для работы с контактами.
</li>
</ol>
<p>
Дополнительные сведения об элементе <code>&lt;ContactsAccountType&gt;</code> представлены в разделе
<a href="#SocialStreamAcctType">элемент &lt;ContactsAccountType&gt;</a>.
</p>
<p>
Данные во входящем намерении содержат URI контента элемента или фотографии, выбранных пользователем.
Чтобы использовать разные операции для текстовых элементов и фотографий, используйте оба атрибута в одном файле.
</p>
<h4>Взаимодействие со службой социальной сети</h4>
<p>
Пользователям не обязательно выходить из приложения для работы с контактами, которое имеется на устройстве, чтобы пригласить контакт на сайт
социальной сети. Вместо этого приложение для работы с контактами может отправить намерение для приглашения
контакта в одну из ваших операций. Для этого выполните указанные ниже действия.
</p>
<ol>
<li>
В каталоге <code>res/xml/</code> своего проекта создайте файл
<code>contacts.xml</code>. Если у вас уже есть этот файл, переходите к следующему действию.
</li>
<li>
В этом файле добавьте элемент
<code>&lt;ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"&gt;</code>.
Если этот элемент уже существует, можете переходить к следующему действию.
</li>
<li>
Добавьте следующие атрибуты:
<ul>
<li><code>inviteContactActivity="<em>activityclass</em>"</code>;</li>
<li>
<code>inviteContactActionLabel="&#64;string/<em>invite_action_label</em>"</code>.
</li>
</ul>
Значение <code><em>activityclass</em></code> представляет собой полное имя класса операции,
которая должна получить намерение. Значение<code><em>invite_action_label</em></code>
это текстовая строка, которая отображается в меню <strong>Добавить подключение</strong>
в приложении для работы с контактами.
</li>
</ol>
<p class="note">
<strong>Примечание.</strong> <code>ContactsSource</code> — это устаревшее имя тега для
<code>ContactsAccountType</code>, которое больше не используется.
</p>
<h3 id="ContactsFile">Ссылка contacts.xml</h3>
<p>
В файле <code>contacts.xml</code> содержатся элементы XML, которые управляют взаимодействием вашего
адаптера синхронизации и вашего приложения с поставщиком контактов и приложением для работы с контактами. Эти
элементы описаны в следующих разделах.
</p>
<h4 id="SocialStreamAcctType">Элемент &lt;ContactsAccountType&gt;</h4>
<p>
Элемент <code>&lt;ContactsAccountType&gt;</code> управляет взаимодействием вашего
приложения с приложением для работы с контактами. Ниже представлен его синтаксис.
</p>
<pre>
&lt;ContactsAccountType
xmlns:android="http://schemas.android.com/apk/res/android"
inviteContactActivity="<em>activity_name</em>"
inviteContactActionLabel="<em>invite_command_text</em>"
viewContactNotifyService="<em>view_notify_service</em>"
viewGroupActivity="<em>group_view_activity</em>"
viewGroupActionLabel="<em>group_action_text</em>"
viewStreamItemActivity="<em>viewstream_activity_name</em>"
viewStreamItemPhotoActivity="<em>viewphotostream_activity_name</em>"&gt;
</pre>
<p>
<strong>находится в:</strong>
</p>
<p>
<code>res/xml/contacts.xml</code>
</p>
<p>
<strong>может содержать:</strong>
</p>
<p>
<strong><code>&lt;ContactsDataKind&gt;</code></strong>
</p>
<p>
<strong>Описание</strong>
</p>
<p>
Этот элемент объявляет компоненты и элементы пользовательского интерфейса, с помощью которых пользователи могут приглашать свои контакты
в социальную сеть, уведомлять пользователей при обновлении одного из их потоков данных из социальных сетей и
др.
</p>
<p>
Обратите внимание, что префикс атрибута <code>android:</code> необязательно использовать для атрибутов
<code>&lt;ContactsAccountType&gt;</code>.
</p>
<p>
<strong>Атрибуты</strong>
</p>
<dl>
<dt>{@code inviteContactActivity}</dt>
<dd>
Полное имя класса операции в вашем приложении, которую необходимо активировать
при выборе пользователем элемента <strong>Добавить подключение</strong> в приложении
для работы с контактами, которое имеется на устройстве.
</dd>
<dt>{@code inviteContactActionLabel}</dt>
<dd>
Текстовая строка, которая отображается для операции, заданной в
{@code inviteContactActivity}, в меню <strong>Добавить подключение</strong>.
Например, можно указать фразу «Следите за новостями в моей сети». Для этого элемента можно использовать
идентификатор строкового ресурса.
</dd>
<dt>{@code viewContactNotifyService}</dt>
<dd>
Полное имя класса службы в вашем приложении, которая должна получать
уведомления при просмотре контакта пользователем. Такое уведомление отправляется приложением для работы с контактами,
которое имеется на устройстве; благодаря этому ваше приложение может отложить выполнение операций, требующих обработки большого объема данных, до тех пор
, пока это не потребуется. Например, ваше приложение может реагировать на такое уведомление
путем считывания и отображения фотографии контакта в высоком разрешении и самых актуальных
элементов потока данных из социальной сети. Дополнительные сведения об этой функции представлены в разделе
<a href="#SocialStreamInteraction">Взаимодействие с потоками данных из социальных сетей</a>. Пример
службы уведомлений представлен в файле
<code>NotifierService.java</code> в
образце приложения<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">SampleSyncAdapter</a>.
</dd>
<dt>{@code viewGroupActivity}</dt>
<dd>
Полное имя класса операции в вашем приложении, которая может отобразить
информацию о группе. При нажатии пользователем на метку группы в приложении для работы с данными,
которое имеется на устройстве, отображается пользовательский интерфейс для этой операции.
</dd>
<dt>{@code viewGroupActionLabel}</dt>
<dd>
Метка, отображаемая приложением для работы с контактами для элемента пользовательского интерфейса, с помощью которой
пользователь может просмотреть группы в вашем приложении.
<p>
Например, если вы установили приложение Google+ на ваше устройство и выполняете синхронизацию
данных в Google+ с приложением для работы с контактами, то круги Google+ будут обозначены в приложении для работы с контактами как
группы на вкладке <strong>Группы</strong>. При нажатии на на круг
Google+ участники крга отобразятся как группа контактов. В верхней части экрана
находится значок Google+; если нажать на него, управление перейдет в приложение
Google+. В приложении для управления контактами это реализовано с помощью
{@code viewGroupActivity}, в которой значок Google+ используется в качестве значения
{@code viewGroupActionLabel}.
</p>
<p>
Для этого атрибута можно использовать идентификатор строкового ресурса.
</p>
</dd>
<dt>{@code viewStreamItemActivity}</dt>
<dd>
Полное имя класса операции в вашем приложении, которую запускает
приложение для работы с контактами, когда пользователь выбирает элемент потока для необработанного контакта.
</dd>
<dt>{@code viewStreamItemPhotoActivity}</dt>
<dd>
Полное имя класса операции в вашем приложении, которую запускает
приложение для работы с контактами, когда пользователь выбирает фотографию в элементе
потока для необработанного контакта.
</dd>
</dl>
<h4 id="SocialStreamDataKind">Элемент &lt;ContactsDataKind&gt;</h4>
<p>
Элемент <code>&lt;ContactsDataKind&gt;</code> управляет отображением настраиваемых строк данных вашего
приложения в интерфейсе приложения для работы с контактами, которое имеется на устройстве. Ниже представлен его синтаксис.
</p>
<pre>
&lt;ContactsDataKind
android:mimeType="<em>MIMEtype</em>"
android:icon="<em>icon_resources</em>"
android:summaryColumn="<em>column_name</em>"
android:detailColumn="<em>column_name</em>"&gt;
</pre>
<p>
<strong>находится в:</strong>
</p>
<code>&lt;ContactsAccountType&gt;</code>
<p>
<strong>Описание</strong>
</p>
<p>
Используйте этот элемент для отображения содержимого настраиваемой строки данных в приложении для работы с контактами как части
сведений о необработанном контакте. Каждый дочерний элемент <code>&lt;ContactsDataKind&gt;</code>
элемента <code>&lt;ContactsAccountType&gt;</code> представляет собой тип настраиваемой строки данных, который
адаптер синхронизации добавляет в таблицу {@link android.provider.ContactsContract.Data}. Для каждого используемого вами настраиваемого типа MIME необходимо добавить один элемент
<code>&lt;ContactsDataKind&gt;</code>. Вам не нужно добавлять элемент
, если у вас имеется настраиваемая строка данных, для которой не требуется отображать данные.
</p>
<p>
<strong>Атрибуты</strong>
</p>
<dl>
<dt>{@code android:mimeType}</dt>
<dd>
Определенные вами настраиваемые типы MIME для одного из ваших типов настраиваемых строк данных в таблице
{@link android.provider.ContactsContract.Data}. Например, значение
<code>vnd.android.cursor.item/vnd.example.locationstatus</code> может быть настраиваемым
типом MIME для строки данных, в которой находятся записи о последнем известном местоположении контакта.
</dd>
<dt>{@code android:icon}</dt>
<dd>
<a href="{@docRoot}guide/topics/resources/drawable-resource.html">Графический ресурс </a>
Android,
который приложение для работы с контактами отображает рядом с вашими данными. Используйте его для обозначения того,
что эти данные получены из вашей службы.
</dd>
<dt>{@code android:summaryColumn}</dt>
<dd>
Имя столбца для первого из двух значений, полученных из строки данных. Значение
отображается в виде первой строки записи в этой строке данных. Первая строка
предназначена для использования в качестве сводных данных, однако она необязательна. См. также
<a href="#detailColumn">android:detailColumn</a>.
</dd>
<dt>{@code android:detailColumn}</dt>
<dd>
Имя столбца для второго из двух значений, полученных из строки данных. Значение
отображается в виде второй строки записи в этой строке данных. См. также
{@code android:summaryColumn}.
</dd>
</dl>
<h2 id="AdditionalFeatures">Дополнительные возможности поставщика контактов</h2>
<p>
Помимо основных функций, описанных разделах выше, в поставщике
контактов предусмотрены указанные ниже полезные функции для работы с данными контактов.
</p>
<ul>
<li>Группы контактов</li>
<li>Функции работы с фотографиями</li>
</ul>
<h3 id="Groups">Группы контактов</h3>
<p>
Поставщик контактов может дополнительно отметить коллекции связанных контактов с данными о
<strong>группе</strong>. Если серверу, который связан с учетной записью пользователя,
требуется сохранить группы, адаптеру синхронизации для типа этого аккаунта следует передать
данные о группах из поставщика контактов на сервер. При добавлении пользователем нового контакта на сервер
и последующем помещении этого контакта в новую группу адаптер синхронизации должен добавить эту новую группу в таблицу
{@link android.provider.ContactsContract.Groups}. Группа или группы, в которые входит необработанный контакт,
хранятся в таблице {@link android.provider.ContactsContract.Data} с использованием типа MIME
{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}.
</p>
<p>
Если необходимо создать адаптер синхронизации, который будет добавлять данные необработанного контакта с сервера
в поставщик контактов, а вы не используете группы, то вам необходимо указать для поставщика,
чтобы он сделал ваши данные видимыми. В коде, который выполняется при добавлении пользователем
аккаунта на устройство, обновите строку {@link android.provider.ContactsContract.Settings},
которую поставщик контактов добавляет для этого аккаунта. В этой строке укажите в столбце
{@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE
Settings.UNGROUPED_VISIBLE} значение «1». После этого поставщик контактов всегда будет
делать ваши данные видимыми, даже если вы не используете группы.
</p>
<h3 id="Photos">Фотографии контактов</h3>
<p>
В таблице {@link android.provider.ContactsContract.Data} хранятся фотографии в виде строк
{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE
Photo.CONTENT_ITEM_TYPE} типа MIME. Столбец
{@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} в строке связан со столбцом
{@code android.provider.BaseColumns#_ID} необработанного контакта, которому он принадлежит.
Класс {@link android.provider.ContactsContract.Contacts.Photo} определяет вложенную таблицу
{@link android.provider.ContactsContract.Contacts}, в которой содержится информация об основной фотографии
контакта (которая является основной фотографией основного необработанного контакта этого контакта). Аналогичным образом класс
{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} определяет вложенную таблицу
{@link android.provider.ContactsContract.RawContacts}, в которой содержится информация об основной фотографии
необработанного контакта.
</p>
<p>
В справочной документации по {@link android.provider.ContactsContract.Contacts.Photo} и
{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} содержатся примеры
получения информации о фотографии. К сожалению, отсутствует класс для удобного извлечения миниатюры
основной фотографии необработанного контакта, однако вы можете отправить запрос в таблицу
{@link android.provider.ContactsContract.Data}, выбрать
{@code android.provider.BaseColumns#_ID} необработанного контакта,
{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE
Photo.CONTENT_ITEM_TYPE} и столбец {@link android.provider.ContactsContract.Data#IS_PRIMARY},
чтобы найти строку основной фотографии необработанного контакта.
</p>
<p>
Потоки данных из социальных сетей также могут включать фотографии. Они находятся в таблице
{@code android.provider.ContactsContract.StreamItemPhotos}, дополнительные сведения о которой представлены в разделе
<a href="#StreamPhotos">Фотографии из потока данных из социальных сетей</a>.
</p>