Продолжим работу с проектом из прошлой темы и добавим в него возможность добавления новых контактов. Добавление контактов представляет собой запрос на изменение списка контактов, то есть его запись. Поэтому нам надо установить соответствующее разрешение в файле манифеста. Возьмем проект из прошлой темы и добавим в него в файл 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);
Перед запуском, если ранее (в прошлой теме) приложение было установлено, то его необходимо удалить, чтобы установить для приложения новые разрешения (разрешение на запись контактов).
Запустим приложение и добавим новый контакт: