xamarin.formsmauimaui-androidmaui-ios

MAUI: Save contact to device


I am trying to save a phone number to the local device in MAUI by Conditional Compilation.

Below is my code:

#if ANDROID

using Android.App;
using Android.Content;
using Android.OS;
using Android.Provider;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using MyApp.Droid.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

#elif IOS

using AddressBook;
using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using UIKit;
using MyApp.iOS.Services;

#endif

namespace MyApp.Renderers
{
    public class SaveContact
    {
        public void SaveContactToPhoneBook(string Name, string Number, string Email)
        {
#if ANDROID
            var activity = Android.App.Application.Context as Activity;
            var intent = new Intent(Intent.ActionInsert).SetFlags(ActivityFlags.ClearTask);
            intent.SetType(ContactsContract.Contacts.ContentType);
            intent.PutExtra(ContactsContract.Intents.Insert.Name, Name);
            intent.PutExtra(ContactsContract.Intents.Insert.Phone, Number);
            intent.PutExtra(ContactsContract.Intents.Insert.Email, Email);
            activity.StartActivity(intent);
#elif IOS
            ABAddressBook ab = new ABAddressBook();
            ABPerson p = new ABPerson();
            p.FirstName = Name;
            ABMutableMultiValue<string> phones = new ABMutableStringMultiValue();
            phones.Add(Number, ABPersonPhoneLabel.Mobile);
            p.SetPhones(phones);
            ABMutableDictionaryMultiValue addresses = new ABMutableDictionaryMultiValue();
            NSMutableDictionary a = new NSMutableDictionary();
            addresses.Add(a, new NSString("Home"));
            p.SetAddresses(addresses);
            ab.Add(p);
            ab.Save();
#endif
        }
    }
}

Then from my contacts save page:

SaveContact saveContact = new SaveContact();
saveContact.SaveContactToPhoneBook("", phone, "");//passing name, phone and email

I am getting exception for this from both Android and iOS platforms.

Android Exception:

System.NullReferenceException: Object reference not set to an instance of an object. at MyProject.Renderers.SaveContact.SaveContactToPhoneBook(String Name, String Number, String Email) in E:\My Projects\MAUI\MyProject-maui\MyProject\Renderers\SaveContact.cs:line 43 [SurfaceComposerClient] XDR setRegion is not supported 0xb4000075d1bc0cf0, 1, 0

iOS Exception:

CoreFoundation.CFException: The operation couldn’t be completed. (ABAddressBookErrorDomain error 1.) at AddressBook.ABAddressBook.Add(ABRecord ) at MyProject.Renderers.SaveContact.SaveContactToPhoneBook(String Name, String Number, String Email) in /Users/MyCompany/Projects/MyProject-maui/MyProject/Renderers/SaveContact.cs:line 55

The codes I have used are from Xamarin Forms Dependency Service and I changed it to Conditional Compilation for MAUI integration.


Solution

  • I tried to achieve this function for Android platform.

    You can refer to the following code:

        public class SaveContact
        {
            public void SaveContactToPhoneBook(string displayName, string Number1, string Email)
            {
    #if ANDROID
    
                List<ContentProviderOperation> ops = new List<ContentProviderOperation>();
    
                ContentProviderOperation.Builder builder =
        ContentProviderOperation.NewInsert(ContactsContract.RawContacts.ContentUri);
                builder.WithValue(ContactsContract.RawContacts.InterfaceConsts.AccountType, null);
                builder.WithValue(ContactsContract.RawContacts.InterfaceConsts.AccountName, null);
                ops.Add(builder.Build());
    
                //Name  
                builder = ContentProviderOperation.NewInsert(ContactsContract.Data.ContentUri);
                builder.WithValueBackReference(ContactsContract.Data.InterfaceConsts.RawContactId, 0);
                builder.WithValue(ContactsContract.Data.InterfaceConsts.Mimetype,
                                  ContactsContract.CommonDataKinds.StructuredName.ContentItemType);
                builder.WithValue(ContactsContract.CommonDataKinds.StructuredName.DisplayName, displayName);
                //builder.WithValue(ContactsContract.CommonDataKinds.StructuredName.GivenName, firstName);  
                ops.Add(builder.Build());
    
                //Number1  
                builder = ContentProviderOperation.NewInsert(ContactsContract.Data.ContentUri);
                builder.WithValueBackReference(ContactsContract.Data.InterfaceConsts.RawContactId, 0);
                builder.WithValue(ContactsContract.Data.InterfaceConsts.Mimetype,
                                  ContactsContract.CommonDataKinds.Phone.ContentItemType);
                builder.WithValue(ContactsContract.CommonDataKinds.Phone.Number, Number1);
                builder.WithValue(ContactsContract.CommonDataKinds.Phone.InterfaceConsts.Type,
                                  ContactsContract.CommonDataKinds.Phone.InterfaceConsts.TypeCustom);
                builder.WithValue(ContactsContract.CommonDataKinds.Phone.InterfaceConsts.Data2, (int)PhoneDataKind.Mobile);
    
                ops.Add(builder.Build());
    
    
    
                //Add the new contact
                ContentProviderResult[] res;
                try
                {
                    res =  Android.App.Application.Context.ContentResolver.ApplyBatch(ContactsContract.Authority, ops);
                    ops.Clear();//Add this line  
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine( e.Message);
                }
    
    #elif IOS
    
     
    #endif
            }
        }
    

    And we need to check and request the write contacts permission on the shared platform.

    private async void SaveContactButton_Clicked(object sender, EventArgs e)
    {
        await RequestWriteContactPermission();
    }
    
    // write contact
    async Task RequestWriteContactPermission()
    {
    
        var status = PermissionStatus.Unknown;
    
        {
            status = await Permissions.CheckStatusAsync<Permissions.ContactsWrite>();
    
            if (status == PermissionStatus.Granted)
            {
                SaveContact saveContact = new SaveContact();
                saveContact.SaveContactToPhoneBook("Test", "123999999", "");//passing name, phone and email
                return;
            }
    
            if (Permissions.ShouldShowRationale<Permissions.Phone>())
            {
                await Shell.Current.DisplayAlert("Needs permissions", "BECAUSE!!!", "OK");
            }
    
            status = await Permissions.RequestAsync<Permissions.ContactsWrite>();
    
        }
    
    
        if (status != PermissionStatus.Granted)
            await Shell.Current.DisplayAlert("Permission required",
                "Write Contact permission is required for test. " +
                "We just want to do a test.", "OK");
    
        else if (status == PermissionStatus.Granted)
        {
            SaveContact saveContact = new SaveContact();
            saveContact.SaveContactToPhoneBook("Test", "123999999", "");//passing name, phone and email
        }
    }
    
    
    public async Task<PermissionStatus> CheckAndRequestContactsWritePermission()
    {
        PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.ContactsWrite>();
    
        if (status == PermissionStatus.Granted)
            return status;
    
        if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
        {
            // Prompt the user to turn on in settings
            // On iOS once a permission has been denied it may not be requested again from the application
            return status;
        }
    
        if (Permissions.ShouldShowRationale<Permissions.ContactsWrite>())
        {
            // Prompt the user with additional information as to why the permission is needed
    
            await Shell.Current.DisplayAlert("Need permission", "We need the Write Contact permission", "Ok");
        }
    
        status = await Permissions.RequestAsync<Permissions.ContactsWrite>();
    
        if (status == PermissionStatus.Granted)
        {
    
            SaveContact saveContact = new SaveContact();
            saveContact.SaveContactToPhoneBook("Test", "123999999", "");//passing name, phone and email
    
        }
        else
        {
            await Shell.Current.DisplayAlert("Permission required",
                  "Write Contact permission is required for test . " +
                  "We just want to test", "OK");
    
        }
    
        return status;
    }
    

    Note:

    Remember to add the following permission on AndroidManifest.xml

       <uses-permission android:name="android.permission.WRITE_CONTACTS" />