Продолжим работу с проектом из прошлой темы и добавим в него возможность добавления новых контактов. Добавление контактов представляет собой запрос на изменение списка контактов, то есть его запись. Поэтому нам надо установить соответствующее разрешение в файле манифеста. Возьмем проект из прошлой темы и добавим в него в файл AndroidManifest.xml разрешение android.permission.WRITE_CONTACTS:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.contactsapp"> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.ContactsApp"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Для добавления контакта добавим изменим файл activity_main.xml, определив в нем текстовое поле для ввода данных:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/newContact" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintBottom_toTopOf="@id/header" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@id/addBtn" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/addBtn" android:text="Add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onAddContact" app:layout_constraintBottom_toTopOf="@id/header" app:layout_constraintLeft_toRightOf="@id/newContact" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/header" android:layout_width="0dp" android:layout_height="wrap_content" android:text="Контакты" android:textSize="18sp" app:layout_constraintBottom_toTopOf="@id/contactList" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/newContact" /> <ListView android:id="@+id/contactList" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/header" /> </androidx.constraintlayout.widget.ConstraintLayout>
В коде MainActivity пропишем обработчик onAddContact
с добавлением контакта:
package com.example.contactsapp; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private static final int REQUEST_CODE_READ_CONTACTS=1; private static boolean READ_CONTACTS_GRANTED =false; ArrayList<String> contacts = new ArrayList<>(); Button addBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); addBtn = findViewById(R.id.addBtn); // получаем разрешения int hasReadContactPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS); // если устройство до API 23, устанавливаем разрешение if(hasReadContactPermission == PackageManager.PERMISSION_GRANTED){ READ_CONTACTS_GRANTED = true; } else{ // вызываем диалоговое окно для установки разрешений ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_READ_CONTACTS); } // если разрешение установлено, загружаем контакты if (READ_CONTACTS_GRANTED){ loadContacts(); } addBtn.setEnabled(READ_CONTACTS_GRANTED); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){ super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE_READ_CONTACTS) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { READ_CONTACTS_GRANTED = true; } addBtn.setEnabled(READ_CONTACTS_GRANTED); } if(READ_CONTACTS_GRANTED){ loadContacts(); } else{ Toast.makeText(this, "Требуется установить разрешения", Toast.LENGTH_LONG).show(); } } public void onAddContact(View v) { ContentValues contactValues = new ContentValues(); EditText contactText = findViewById(R.id.newContact); String newContact = contactText.getText().toString(); contactText.setText(""); contactValues.put(ContactsContract.RawContacts.ACCOUNT_NAME, newContact); contactValues.put(ContactsContract.RawContacts.ACCOUNT_TYPE, newContact); Uri newUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, contactValues); long rawContactsId = ContentUris.parseId(newUri); contactValues.clear(); contactValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactsId); contactValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); contactValues.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, newContact); getContentResolver().insert(ContactsContract.Data.CONTENT_URI, contactValues); Toast.makeText(getApplicationContext(), newContact + " добавлен в список контактов", Toast.LENGTH_LONG).show(); loadContacts(); } private void loadContacts(){ contacts.clear(); ContentResolver contentResolver = getContentResolver(); Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); if(cursor!=null){ while (cursor.moveToNext()) { // получаем каждый контакт String contact = cursor.getString( cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)); // добавляем контакт в список contacts.add(contact); } cursor.close(); } // создаем адаптер ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contacts); // устанавливаем для списка адаптер ListView contactList = findViewById(R.id.contactList); contactList.setAdapter(adapter); } }
Сразу стоит отметить, что для работы с контактами не надо отдельно получать разрешения на чтение и отдельно на изменение контактов. Пользователь один раз согласие для установки сразу двух разрешений. Однако на уровне кода нам необходимо перечислить через запятую устанавливаемые разрешения:
// вызываем диалоговое окно для установки разрешений ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS }, REQUEST_CODE_READ_CONTACTS);
Однако мы опять же можем управлять разрешением, например, установить доступность кнопки:
addBtn.setEnabled(READ_CONTACTS_GRANTED);
Если разрешение не получено, то переменная READ_CONTACTS_GRANTED будет иметь значение false, и соответственно кнопка будет недоступна, и мы не сможем добавить новый контакт.
Весь код добавления находится в обработчике нажатия кнопки onAddContact
. В Android контакты
распределяются по трем таблицам: contacts, raw contacts и data. И нам надо добавить новый контакт в две последне таблицы. В таблицу contact в силу настроек мы
добавить не можем, но это и не нужно.
Данные контакта представляют объект ContentValues, который состоит из ключей и их значений, то есть объект словаря. После его создания происходит добавление в него пары элементов:
contactValues.put(RawContacts.ACCOUNT_NAME, newContact); contactValues.put(RawContacts.ACCOUNT_TYPE, newContact);
Здесь устанавливается название и тип контакта. В качестве ключей выставляются значения RawContacts.ACCOUNT_NAME
и
RawContacts.ACCOUNT_TYPE
, а в качестве их значения - текст из текстового поля.
Далее этот объект добавляется в таблицу RawContacts с помощью метода insert()
:
Uri newUri = getContentResolver().insert(RawContacts.CONTENT_URI, contactValues);
Метод insert() возвращает URI - ссылку на добавленный объект в таблице, у которого мы можем получить id. Затем после очистки мы подготавливаем объект для доабвления в таблицу Data, вновь наполняя его данными:
contactValues.put(Data.RAW_CONTACT_ID, rawContactsId); contactValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); contactValues.put(StructuredName.DISPLAY_NAME, newContact);
И опять добавление производит метод insert():
getContentResolver().insert(Data.CONTENT_URI, contactValues);
Перед запуском, если ранее (в прошлой теме) приложение было установлено, то его необходимо удалить, чтобы установить для приложения новые разрешения (разрешение на запись контактов).
Запустим приложение и добавим новый контакт: