Додавання контактів

Продовжимо роботу з проектом з минулої теми і додамо в нього можливість додавання нових контактів. Додавання контактів являє собою запит на зміну списку контактів, тобто його запис. Тому нам треба встановити відповідний дозвіл у файлі маніфесту. Візьмемо проєкт із минулої теми і додамо в нього у файл 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 із додаванням контакту:

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

Перед запуском, якщо раніше (у минулій темі) застосунок було встановлено, то його необхідно видалити, щоб встановити для застосунку нові дозволи (дозвіл на запис контактів).

Запустимо додаток і додамо новий контакт: