Провайдеры контента

Последнее обновление: 18.10.2021

Наше приложение может сохранять разнообразую информацию о пользователе, какие-то связанные данные в файлах или настройках. Однако ОС Android уже хранит ряд важной информации, связанной с пользователем, к которой имеем доступ и которую мы можем использовать. Это и списки контактов, и файлы сохраненных изображений и видеоматериалов, и какие-то отметки о звонках и т.д., то есть некоторый контент. А для доступа к этому контенту в OC Android определены провайдеры контента (content provider)

В Android имеются следующие встроенные провайдеры, определенные в пакете android.content:

  • AlarmClock: управление будильником

  • Browser: история браузера и закладки

  • CalendarContract: каледарь и информаци о событиях

  • CallLog: информация о звонках

  • ContactsContract: контакты

  • MediaStore: медиа-файлы

  • SearchRecentSuggestions: подсказки по поиску

  • Settings: системные настройки

  • UserDictionary: словарь слов, которые используются для быстрого набора

  • VoicemailContract: записи голосовой почты

Работа с контактами

Контакты в Android обладают встроенным API, который позволяет получать и изменять список контактов. Все контакты хранятся в базе данных SQLite, однако они не представляют единой таблицы. Для контактов отведено три таблицы, связанных отношением один-ко-многим: таблица для хранения информации о людях, таблица их телефонов и и таблица адресов их электронных почт. Но благодаря Android API мы можем абстрагироваться от связей между таблицами.

Общая форма получения контактов выглядит следующим образом:

ArrayList<String> contacts = new ArrayList<String>();
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();
}

Все контакты и сопутствующий функционал хранятся в специальных базах данных SQLite. Но нам не надо напрямую работать с ними. Мы можем воспользоваться объектом класса Cursor. Чтобы его получить, сначала вызывается метод getContentResolver(), который возвращает объект ContentResolver. Затем по цепочке вызывается метод query(). В этот метод передается ряд параметров, первый из которых представляет URI - ресурс, который мы хотим получить. Для обращения к базе данных контактов используется константа ContactsContract.Contacts.CONTENT_URI

Метод contactsCursor.moveToNext() позволяет последовательно перемещаться по записям контактов, считывая по одному контакту через вызов contactsCursor.getString().

Таким образом, получать контакты не сложно. Главная сложность в работе с контактами, да и с любыми другими провайдерами контента, заключается в установке разрешений. До Android API 23 достаточно было установить соответствующее разрешение в файле манифеста приложения. Начиная же с API 23 (Android Marshmallow) Google изменил схему работы с разрешениями. И теперь пользователь сам должен решить, будет ли он давать разрешения приложению. В связи с чем разработчики должны добавлять дополнительный код.

Итак, для доступа к контактам нам надо установить разрешение android.permission.READ_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"  />
	
    <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">

    <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_toTopOf="parent" />

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

Для вывода списка контактов воспользуемся элементом ListView. И в классе MainActivity получим контакты:

package com.example.contactsapp;

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.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ArrayAdapter;
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;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // получаем разрешения
        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}, REQUEST_CODE_READ_CONTACTS);
        }
        // если разрешение установлено, загружаем контакты
        if (READ_CONTACTS_GRANTED){
            loadContacts();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, 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;
            }
        }
        if(READ_CONTACTS_GRANTED){
            loadContacts();
        }
        else{
            Toast.makeText(this, "Требуется установить разрешения", Toast.LENGTH_LONG).show();
        }
    }

    private void loadContacts(){
        ContentResolver contentResolver = getContentResolver();
        Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
        ArrayList<String> contacts = new ArrayList<String>();

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

Кроме собственно загрузки контактов и передачи их через адаптер ArrayAdapter в список ListView здесь добавлено много кода по управлению разрешениями. Вначале определена переменная READ_CONTACTS_GRANTED, которая указывает, было ли выдано разрешение. И здеь есть два варианта действий.

Первый вариант предполагает, что устройство имеет версию Android ниже Marshmallow (ниже API 23). Для этого мы просто узнаем, есть ли разрешение для READ_CONTACTS и если оно есть, то устанавливаем для переменной READ_CONTACTS_GRANTED значение true:

int hasReadContactPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS);
if(hasReadContactPermission == PackageManager.PERMISSION_GRANTED){
    READ_CONTACTS_GRANTED = true;
}

Иначе нам надо отобразить пользователю диалоговое окно, где он решить, надо ли дать приложению разрешение:

ActivityCompat.requestPermissions(this, 
								new String[]{Manifest.permission.READ_CONTACTS}, 
								REQUEST_CODE_READ_CONTACTS);

В этот метод передаются три параметра. Первый - текущий контекст, то есть текущий объект Activity.

Второй параметр представляет набор разрешений, которые надо получить, в виде массива строк. Нам надо получить в данном случае только одно разрешение - Manifest.permission.READ_CONTACTS.

Третий параметр представляет код запроса, через который мы сможем получить ответ пользователя.

Если мы хотим получить выбор пользователя при установке разрешений, то нам надо переопределить в классе Activity метод onRequestPermissionsResult:

public void onRequestPermissionsResult(int requestCode, String[] permissions, 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;
		}
	}
	if(READ_CONTACTS_GRANTED){
		loadContacts();
	}
	else{
		Toast.makeText(this, "Требуется установить разрешения", Toast.LENGTH_LONG).show();
	}
}

Первый параметр метода requestCode - это тот код запроса, который передавался в качестве третьего параметра в ActivityCompat.requestPermissions(). Второй параметр - массив строк, для которых устанавливались разрешения. То есть одномоментно мы можем устанавливать сразу несколько разрешений.

Третий параметр собственно хранит числовые коды разрешений. Так мы запрашиваем только одно разрешение, то первый элемент массива будет хранить его код. Через условное выражение мы можем проверить этот код: grantResults[0] == PackageManager.PERMISSION_GRANTED. И в зависимости от результата проверки изменить переменную READ_CONTACTS_GRANTED.

И при запуске приложения нам сначала отобразится окно для выдачи разрешения, а после выдачи подтверждений список контактов:

Чтение контактов в Android

После выдачи разрешения при повторных запусках приложения повторять разрешение не нужно, поэтому метод onRequestPermissionsResult() в таком случае будет срабатывать только один раз. А переменная READ_CONTACTS_GRANTED в этом случае уже будет иметь значение true.

Другоая ситуация - если мы отклоним разрешение. В этом случае при повторном запуске приложения повторно будет отображаться данное окно.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850