Разработка под Android в .NET MAUI. Выбор контакта на устройстве

Для выполнения различных операций в Android наше приложение .NET MAUI должно взаимодействовать с другими приложениями. Например, для выбора контакта и набора телефонного номера наше приложение должно вначале открыть приложение «Контакты», чтобы пользователь выбрал необходимый контакт, а затем — открыть приложение «Телефон» с набранным номером контакта. В этой части мы рассмотрим как мы можем взаимодействовать с другими приложениями в Android на примере приложения .NET MAUI «Выбор контакта и набор телефонного номера».

Выбор контакта в Android. Интерфейс IContacts

Интерфейс IContacts содержит методы для работы с контактами:

Метод Описание
public Task<IEnumerable<Contact>> GetAllAsync(CancellationToken cancellationToken = default);
Возвращает список всех контактов на устройстве
public Task<Contact?> PickContactAsync();
Открывает приложение Android по умолчанию для выбора контакта с устройства

Реализация интерфейса по умолчанию содержится в свойстве Default класса Contacts. Воспользуемся этими методами в приложении .NET MAUI. Создадим новый проект с названием MauiPhone.

Определение необходимых разрешений

Для того, чтобы наше приложение могло считывать данные о контактах, необходимо запросить разрешение android.permission.READ_CONTACTS одним из известных нам способов. Например, добавим в MauiApplication.cs атрибут сборки:

using Android.App;
using Android.Runtime;

[assembly: UsesPermission(Android.Manifest.Permission.ReadContacts)]

namespace MauiPhone;

[Application]
public class MainApplication : MauiApplication
{
    public MainApplication(IntPtr handle, JniHandleOwnership ownership)
        : base(handle, ownership)
    {
    }

    protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Вывод списка контактов

Создадим в проекте новую папку ViewModels и добавим в неё класс ContactsViewModel со следующим содержимым:

using System.Collections.ObjectModel;
using System.Windows.Input;

namespace MauiPhone.ViewModels
{
    public class ContactsViewModel
    {
        public ObservableCollection<Contact> ContactList { get; set; } = [];
   
        public ICommand ReadContacts {  get; set; }

        public ContactsViewModel() 
        {
            ReadContacts = new Command(async () => 
            {

                if (await CheckPermissionStatus() == PermissionStatus.Granted)
                {
                    foreach (var contact in await Microsoft.Maui.ApplicationModel.Communication.Contacts.Default.GetAllAsync())
                    {
                        ContactList.Add(contact);
                    }
                }
            });
        }

        public async Task<PermissionStatus> CheckPermissionStatus()
        {
            var currentStatus = await Permissions.CheckStatusAsync<Permissions.ContactsRead>();
            if (currentStatus != PermissionStatus.Granted)
            {
                currentStatus = await Permissions.RequestAsync<Permissions.ContactsRead>();
            }
            return currentStatus;
        }
    }
}

Здесь мы определили свойство ContactList, которое будет содержать список всех контактов

public ObservableCollection<Contact> ContactList { get; set; } = [];

а также команду ReadContacts для получения списка контактов:

public ICommand ReadContacts {  get; set; }
...
ReadContacts = new Command(async () => 
{

    if (await CheckPermissionStatus() == PermissionStatus.Granted)
    {
        foreach (var contact in await Microsoft.Maui.ApplicationModel.Communication.Contacts.Default.GetAllAsync())
        {
            ContactList.Add(contact);
        }
    }
});

Здесь мы вначале проверяем статус разрешения на чтение контактов, используя метод CheckPermissionStatus():

public async Task<PermissionStatus> CheckPermissionStatus()
{
    var currentStatus = await Permissions.CheckStatusAsync<Permissions.ContactsRead>();
    if (currentStatus != PermissionStatus.Granted)
    {
        currentStatus = await Permissions.RequestAsync<Permissions.ContactsRead>();
    }
    return currentStatus;
}

и затем, если разрешение было получено, считываем список всех контактов на устройстве. Используем нашу модель представления на главной странице приложения. Вначале изменим код MainPage.xaml.cs, передав в качестве контекста модель представления:

using MauiPhone.ViewModels;
namespace MauiPhone
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            BindingContext = new ContactsViewModel();
        }
    }
}

Теперь изменим XAML-код страницы MainPage.xaml следующим образом:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiPhone.ViewModels"
             xmlns:model="clr-namespace:Microsoft.Maui.ApplicationModel.Communication;assembly=Microsoft.Maui.Essentials"
             x:Class="MauiPhone.MainPage"
             x:DataType="local:ContactsViewModel">

    <ScrollView>
        <VerticalStackLayout
            Padding="30,0"
            Spacing="25">

            <Button Text="Посмотреть контакты" Command="{Binding ReadContacts}" />
            
            <Picker ItemsSource="{Binding ContactList, Mode=TwoWay}" 
                    ItemDisplayBinding="{Binding DisplayName}" />
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Клик по кнопке запускает команду на получение всего списка контактов, которые после загрузки отобразятся в выпадающем списке Picker. При этом, в списке мы будем отображать свойство DisplayName контакта. Запустим приложение и посмотрим на результат:

Вывод списка контактов

При этом при первой попытке загрузки списка контактов, приложение попросит разрешение на доступ к контактам. С первой частью работы с контактами мы справились — научились загружать и показывать список контактов. Теперь необходимо научиться выбирать контакт и показывать по нему информацию

Выбор контакта из списка и отображение свойств контакта

Каждый элемент списка ContactList нашей модели представления содержит объект типа Contact. Класс Contact предоставляет нам следующую информацию по контакту:

Свойство Тип Описание
Id string Идентификатор контакта
GivenName string Имя контакта
MiddleName string Отчество контакта
FamilyName string Фамилия контакта
DisplayName string Отображаемое имя (Ф.И.О.)
NamePrefix string Префикс имени контакта
NameSuffix string Суффикс имени контакта
Phones List<ContactPhone> Список телефонных номеров
Emails List<ContactEmail> Список адресов Email

Допишем модель представления следующим образом:

public class ContactsViewModel: INotifyPropertyChanged
{
    public ObservableCollection<Contact> ContactList { get; set; } = [];


    private Contact selectedContact;
    public Contact SelectedContact { 
        get => selectedContact; 
        set 
        {
            if (value != selectedContact)
            {
                selectedContact = value;
                OnPropertyChanged();
            }
        } 
    }

    private ContactPhone selectedPhone;
    public ContactPhone SelectedPhone { 
        get => selectedPhone;
        set 
        {
            if (value != selectedPhone) 
            {
                selectedPhone = value;
                OnPropertyChanged();
            }
        }
    }

    private ContactEmail selectedEmail;
    public ContactEmail SelectedEmail
    {
        get => selectedEmail;
        set
        {
            if (value != selectedEmail)
            {
                selectedEmail = value;
                OnPropertyChanged();
            }
        }
    }


    public ICommand ReadContacts {  get; set; }

    public ContactsViewModel() 
    {
        ReadContacts = new Command(async () => 
        {

            if (await CheckPermissionStatus() == PermissionStatus.Granted)
            {
                foreach (var contact in await Microsoft.Maui.ApplicationModel.Communication.Contacts.Default.GetAllAsync())
                {
                    ContactList.Add(contact);
                }
                if (ContactList.Count > 0) 
                {
                    SelectedContact = ContactList[0];
                    
                    OnPropertyChanged(nameof(SelectedContact));
                }
            }
        });
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }


    public async Task<PermissionStatus> CheckPermissionStatus()
    {
        var currentStatus = await Permissions.CheckStatusAsync<Permissions.ContactsRead>();
        if (currentStatus != PermissionStatus.Granted)
        {
            currentStatus = await Permissions.RequestAsync<Permissions.ContactsRead>();
        }
        return currentStatus;
    }
}

Здесь мы добавили три новых свойства:

Выбранный контакт

private Contact selectedContact;
public Contact SelectedContact { 
    get => selectedContact; 
    set 
    {
        if (value != selectedContact)
        {
            selectedContact = value;
            OnPropertyChanged();
        }
    } 
}

Выбранный у контакта номер телефона

private ContactPhone selectedPhone;
public ContactPhone SelectedPhone { 
    get => selectedPhone;
    set 
    {
        if (value != selectedPhone) 
        {
            selectedPhone = value;
            OnPropertyChanged();
        }
    }
}

Выбранный у контакта адрес Email

private ContactEmail selectedEmail;
public ContactEmail SelectedEmail
{
    get => selectedEmail;
    set
    {
        if (value != selectedEmail)
        {
            selectedEmail = value;
            OnPropertyChanged();
        }
    }
}

Для того, чтобы мы могли отслеживать изменения этих свойств, мы реализовали интерфейс INotifyPropertyChanged.

public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}

Теперь используем эти свойства для привязки. Изменим XAML-код страницы следующим образом:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiPhone.ViewModels"
             xmlns:model="clr-namespace:Microsoft.Maui.ApplicationModel.Communication;assembly=Microsoft.Maui.Essentials"
             x:Class="MauiPhone.MainPage"
             x:DataType="local:ContactsViewModel">

    <ScrollView>
        <VerticalStackLayout
            Padding="30,0"
            Spacing="25">

            <Button Text="Посмотреть контакты" Command="{Binding ReadContacts}" />
            
            <Picker ItemsSource="{Binding ContactList, Mode=TwoWay}" 
                    ItemDisplayBinding="{Binding DisplayName}"
                    SelectedItem="{Binding SelectedContact, Mode=TwoWay}"/>

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <Label Text="Отображаемое имя" Grid.Row="0" Grid.Column="0"/>
                <Label Text="Имя" Grid.Row="1" Grid.Column="0"/>
                <Label Text="Отчество" Grid.Row="2" Grid.Column="0"/>
                <Label Text="Фамилия" Grid.Row="3" Grid.Column="0"/>
                <Label Text="Список телефонов" Grid.Row="4" Grid.Column="0" VerticalOptions="Center"/>
                <Label Text="Список Email" Grid.Row="5" Grid.Column="0" VerticalOptions="Center"/>
                <Label Text="{Binding SelectedContact.DisplayName, Mode=TwoWay}" Grid.Row="0" Grid.Column="1" />
                <Label Text="{Binding SelectedContact.GivenName, Mode=TwoWay}" Grid.Row="1" Grid.Column="1"/>
                <Label Text="{Binding SelectedContact.MiddleName, Mode=TwoWay}" Grid.Row="2" Grid.Column="1"/>
                <Label Text="{Binding SelectedContact.FamilyName, Mode=TwoWay}" Grid.Row="3" Grid.Column="1"/>
                <Picker ItemsSource="{Binding SelectedContact.Phones, Mode=TwoWay}" SelectedItem="{Binding SelectedPhone}" Grid.Row="4" Grid.Column="1"/>
                <Picker ItemsSource="{Binding SelectedContact.Emails, Mode=TwoWay}" SelectedItem="{Binding SelectedEmail}" Grid.Row="5" Grid.Column="1"/>
            </Grid>

        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Здесь мы привязываем визуальные компоненты Label и Picker к свойствам контакта. Теперь при выборе контакта в первом списке мы должны увидеть основные свойства выбранного контакта.

Запустим приложение, загрузим список контактов и выберем какой-нибудь контакт в списке:

Выбор контакта
Выбор контакта

Если у контакта есть список телефонов или адресов электронной почты, то они отобразятся в соответствующих списках.

Выбор контакта методом PickContactAsync()

Если вам не требуется загружать весь список контактов в своё приложение, а достаточно просто открыть адресную книгу и выбрать в ней конкретный контакт, то можно воспользоваться методом PickContactAsync(). Добавим в модель представления ещё одну команду:

public ICommand PickContact { get; set; }       

public ContactsViewModel() 
{
    PickContact = new Command(async () => 
    {
        var contact = await Microsoft.Maui.ApplicationModel.Communication.Contacts.Default.PickContactAsync();
        if (contact != null) 
        {
            SelectedContact = contact;
        }
    });

   ...
}

Здесь мы вызываем метод PickContactAsync() и, если результатом будет не null, то выбранный контакт будет присвоен свойству SelectedContact, что приведен к отображению свойств контакта в приложении.

Добавим на страницу MainPage новую кнопку и сделаем необходимую привязку к команде:

<Button Text="Выбрать контакт из адресной книги" Command="{Binding PickContact}" />

Запустим приложение и проверим результат. При нажатии на кнопку «Выбрать контакт из адресной книги» операционная система предложит выбор приложения для контактов (если на устройстве таких приложений несколько):

Выбор контакта

После выбора, откроется соответствующее приложение с контактами. Если контакт будет выбран, то в приложении вы увидите его свойства также, как и на рисунке выше.

Итого

Для выбора контактов в Android используется интерфейс IContacts, реализация по умолчанию которого содержится в свойстве Default статического класса Contacts. При этом мы можем как загрузить весь список контактов в приложение, так и выбрать конкретный контакт, воспользовавшись методом PickContactAsync()

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии