Xamarin.Forms讀取並展示Android和iOS通訊錄 TerminalMACS客戶端 本文同步更新地址: https://dotnet9.com/11520.html https://terminalmacs.com/861.html 閱讀導航: 一、功能說明 二、代碼實現 三、源碼獲取 ...
Xamarin.Forms讀取並展示Android和iOS通訊錄 - TerminalMACS客戶端
本文同步更新地址:
閱讀導航:
- 一、功能說明
- 二、代碼實現
- 三、源碼獲取
- 四、參考資料
- 五、後面計劃
一、功能說明
完整思維導圖:https://github.com/dotnet9/TerminalMACS/blob/master/docs/TerminalMACS.xmind
本文介紹圖中右側畫紅圈處的功能,即使用Xamarin.Forms獲取和展示Android和iOS的通訊錄信息,下麵是最終效果,由於使用的是真實手機,所以聯繫人姓名及電話號碼打碼顯示。
並簡單的進行了搜索功能處理,之所以說簡單,是因為通訊錄列表是全部讀取出來了,搜索是直接從此列表進行過濾的。
下圖來自:https://www.xamboy.com/2019/10/10/getting-phone-contacts-in-xamarin-forms/, 本功能是參考此文所寫,所以直接引用文中的圖片。
二、代碼實現
1、共用庫工程創建聯繫人實體類:Contacts.cs
namespace TerminalMACS.Clients.App.Models
{
/// <summary>
/// 通訊錄
/// </summary>
public class Contact
{
/// <summary>
/// 獲取或者設置名稱
/// </summary>
public string Name { get; set; }
/// <summary>
/// 獲取或者設置 頭像
/// </summary>
public string Image { get; set; }
/// <summary>
/// 獲取或者設置 郵箱地址
/// </summary>
public string[] Emails { get; set; }
/// <summary>
/// 獲取或者設置 手機號碼
/// </summary>
public string[] PhoneNumbers { get; set; }
}
}
2、共用庫創建通訊錄服務介面:IContactsService.cs
包括:
- 一個通訊錄獲取請求介面:RetrieveContactsAsync
- 一個讀取一條通訊結果通知事件:OnContactLoaded
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using TerminalMACS.Clients.App.Models;
namespace TerminalMACS.Clients.App.Services
{
/// <summary>
/// 通訊錄事件參數
/// </summary>
public class ContactEventArgs:EventArgs
{
public Contact Contact { get; }
public ContactEventArgs(Contact contact)
{
Contact = contact;
}
}
/// <summary>
/// 通訊錄服務介面,android和iOS終端具體的通訊錄獲取服務需要繼承此介面
/// </summary>
public interface IContactsService
{
/// <summary>
/// 讀取一條數據通知
/// </summary>
event EventHandler<ContactEventArgs> OnContactLoaded;
/// <summary>
/// 是否正在載入
/// </summary>
bool IsLoading { get; }
/// <summary>
/// 嘗試獲取所有通訊錄
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? token = null);
}
}
3、iOS工程中添加通訊錄服務,實現IContactsService介面:
using Contacts;
using Foundation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TerminalMACS.Clients.App.Models;
using TerminalMACS.Clients.App.Services;
namespace TerminalMACS.Clients.App.iOS.Services
{
/// <summary>
/// 通訊錄獲取服務
/// </summary>
public class ContactsService : NSObject, IContactsService
{
const string ThumbnailPrefix = "thumb";
bool requestStop = false;
public event EventHandler<ContactEventArgs> OnContactLoaded;
bool _isLoading = false;
public bool IsLoading => _isLoading;
/// <summary>
/// 非同步請求許可權
/// </summary>
/// <returns></returns>
public async Task<bool> RequestPermissionAsync()
{
var status = CNContactStore.GetAuthorizationStatus(CNEntityType.Contacts);
Tuple<bool, NSError> authotization = new Tuple<bool, NSError>(status == CNAuthorizationStatus.Authorized, null);
if (status == CNAuthorizationStatus.NotDetermined)
{
using (var store = new CNContactStore())
{
authotization = await store.RequestAccessAsync(CNEntityType.Contacts);
}
}
return authotization.Item1;
}
/// <summary>
/// 非同步請求通訊錄,此方法由界面真正調用
/// </summary>
/// <param name="cancelToken"></param>
/// <returns></returns>
public async Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? cancelToken = null)
{
requestStop = false;
if (!cancelToken.HasValue)
cancelToken = CancellationToken.None;
// 我們創建了一個十進位的TaskCompletionSource
var taskCompletionSource = new TaskCompletionSource<IList<Contact>>();
// 在cancellationToken中註冊lambda
cancelToken.Value.Register(() =>
{
// 我們收到一條取消消息,取消TaskCompletionSource.Task
requestStop = true;
taskCompletionSource.TrySetCanceled();
});
_isLoading = true;
var task = LoadContactsAsync();
// 等待兩個任務中的第一個任務完成
var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);
_isLoading = false;
return await completedTask;
}
/// <summary>
/// 非同步載入通訊錄,具體的通訊錄讀取方法
/// </summary>
/// <returns></returns>
async Task<IList<Contact>> LoadContactsAsync()
{
IList<Contact> contacts = new List<Contact>();
var hasPermission = await RequestPermissionAsync();
if (hasPermission)
{
NSError error = null;
var keysToFetch = new[] { CNContactKey.PhoneNumbers, CNContactKey.GivenName, CNContactKey.FamilyName, CNContactKey.EmailAddresses, CNContactKey.ImageDataAvailable, CNContactKey.ThumbnailImageData };
var request = new CNContactFetchRequest(keysToFetch: keysToFetch);
request.SortOrder = CNContactSortOrder.GivenName;
using (var store = new CNContactStore())
{
var result = store.EnumerateContacts(request, out error, new CNContactStoreListContactsHandler((CNContact c, ref bool stop) =>
{
string path = null;
if (c.ImageDataAvailable)
{
path = path = Path.Combine(Path.GetTempPath(), $"{ThumbnailPrefix}-{Guid.NewGuid()}");
if (!File.Exists(path))
{
var imageData = c.ThumbnailImageData;
imageData?.Save(path, true);
}
}
var contact = new Contact()
{
Name = string.IsNullOrEmpty(c.FamilyName) ? c.GivenName : $"{c.GivenName} {c.FamilyName}",
Image = path,
PhoneNumbers = c.PhoneNumbers?.Select(p => p?.Value?.StringValue).ToArray(),
Emails = c.EmailAddresses?.Select(p => p?.Value?.ToString()).ToArray(),
};
if (!string.IsNullOrWhiteSpace(contact.Name))
{
OnContactLoaded?.Invoke(this, new ContactEventArgs(contact));
contacts.Add(contact);
}
stop = requestStop;
}));
}
}
return contacts;
}
}
}
4、在iOS工程中的Info.plist文件添加通訊錄許可權使用說明
5、在Android工程中添加讀取通訊錄許可權配置:AndroidManifest.xml
<uses-permission android:name="android.permission.READ_CONTACTS"/>
完整許可權配置如下
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.terminalmacs.clients.app">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
<application android:label="TerminalMACS.Clients.App.Android"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
6、在Android工程中添加通訊錄服務,實現IContactServer介面:ContactsService.cs
using Acr.UserDialogs;
using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Database;
using Android.Provider;
using Android.Runtime;
using Android.Support.V4.App;
using Plugin.CurrentActivity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TerminalMACS.Clients.App.Models;
using TerminalMACS.Clients.App.Services;
namespace TerminalMACS.Clients.App.Droid.Services
{
/// <summary>
/// 通訊錄獲取服務
/// </summary>
public class ContactsService : IContactsService
{
const string ThumbnailPrefix = "thumb";
bool stopLoad = false;
static TaskCompletionSource<bool> contactPermissionTcs;
public string TAG
{
get
{
return "MainActivity";
}
}
bool _isLoading = false;
public bool IsLoading => _isLoading;
//許可權請求狀態碼
public const int RequestContacts = 1239;
/// <summary>
/// 獲取通訊錄需要的請求許可權
/// </summary>
static string[] PermissionsContact = {
Manifest.Permission.ReadContacts
};
public event EventHandler<ContactEventArgs> OnContactLoaded;
/// <summary>
/// 非同步請求通訊錄許可權
/// </summary>
async void RequestContactsPermissions()
{
//檢查是否可以彈出申請讀、寫通訊錄許可權
if (ActivityCompat.ShouldShowRequestPermissionRationale(CrossCurrentActivity.Current.Activity, Manifest.Permission.ReadContacts)
|| ActivityCompat.ShouldShowRequestPermissionRationale(CrossCurrentActivity.Current.Activity, Manifest.Permission.WriteContacts))
{
// 如果未授予許可,請向用戶提供其他理由用戶將從使用許可權的附加上下文中受益。
// 例如,如果請求先前被拒絕。
await UserDialogs.Instance.AlertAsync("通訊錄許可權", "此操作需要“通訊錄”許可權", "確定");
}
else
{
// 尚未授予通訊錄許可權。直接請求這些許可權。
ActivityCompat.RequestPermissions(CrossCurrentActivity.Current.Activity, PermissionsContact, RequestContacts);
}
}
/// <summary>
/// 收到用戶響應請求許可權操作後的結果
/// </summary>
/// <param name="requestCode"></param>
/// <param name="permissions"></param>
/// <param name="grantResults"></param>
public static void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
if (requestCode == RequestContacts)
{
// 我們請求了多個通訊錄許可權,因此需要檢查相關的所有許可權
if (PermissionUtil.VerifyPermissions(grantResults))
{
// 已授予所有必需的許可權,顯示聯繫人片段。
contactPermissionTcs.TrySetResult(true);
}
else
{
contactPermissionTcs.TrySetResult(false);
}
}
}
/// <summary>
/// 非同步請求許可權
/// </summary>
/// <returns></returns>
public async Task<bool> RequestPermissionAsync()
{
contactPermissionTcs = new TaskCompletionSource<bool>();
// 驗證是否已授予所有必需的通訊錄許可權。
if (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.ReadContacts) != (int)Permission.Granted
|| Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.WriteContacts) != (int)Permission.Granted)
{
// 尚未授予通訊錄許可權。
RequestContactsPermissions();
}
else
{
// 已授予通訊錄許可權。
contactPermissionTcs.TrySetResult(true);
}
return await contactPermissionTcs.Task;
}
/// <summary>
/// 非同步請求通訊錄,此方法由界面真正調用
/// </summary>
/// <param name="cancelToken"></param>
/// <returns></returns>
public async Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? cancelToken = null)
{
stopLoad = false;
if (!cancelToken.HasValue)
cancelToken = CancellationToken.None;
// 我們創建了一個十進位的TaskCompletionSource
var taskCompletionSource = new TaskCompletionSource<IList<Contact>>();
// 在cancellationToken中註冊lambda
cancelToken.Value.Register(() =>
{
// 我們收到一條取消消息,取消TaskCompletionSource.Task
stopLoad = true;
taskCompletionSource.TrySetCanceled();
});
_isLoading = true;
var task = LoadContactsAsync();
// 等待兩個任務中的第一個任務完成
var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);
_isLoading = false;
return await completedTask;
}
/// <summary>
/// 非同步載入通訊錄,具體的通訊錄讀取方法
/// </summary>
/// <returns></returns>
async Task<IList<Contact>> LoadContactsAsync()
{
IList<Contact> contacts = new List<Contact>();
var hasPermission = await RequestPermissionAsync();
if (!hasPermission)
{
return contacts;
}
var uri = ContactsContract.Contacts.ContentUri;
var ctx = Application.Context;
await Task.Run(() =>
{
// 暫時只請求通訊錄Id、DisplayName、PhotoThumbnailUri,可以擴展
var cursor = ctx.ApplicationContext.ContentResolver.Query(uri, new string[]
{
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName,
ContactsContract.Contacts.InterfaceConsts.PhotoThumbnailUri
}, null, null, $"{ContactsContract.Contacts.InterfaceConsts.DisplayName} ASC");
if (cursor.Count > 0)
{
while (cursor.MoveToNext())
{
var contact = CreateContact(cursor, ctx);
if (!string.IsNullOrWhiteSpace(contact.Name))
{
// 讀取出一條,即通知界面展示
OnContactLoaded?.Invoke(this, new ContactEventArgs(contact));
contacts.Add(contact);
}
if (stopLoad)
break;
}
}
});
return contacts;
}
/// <summary>
/// 讀取一條通訊錄數據
/// </summary>
/// <param name="cursor"></param>
/// <param name="ctx"></param>
/// <returns></returns>
Contact CreateContact(ICursor cursor, Context ctx)
{
var contactId = GetString(cursor, ContactsContract.Contacts.InterfaceConsts.Id);
var numbers = GetNumbers(ctx, contactId);
var emails = GetEmails(ctx, contactId);
var uri = GetString(cursor, ContactsContract.Contacts.InterfaceConsts.PhotoThumbnailUri);
string path = null;
if (!string.IsNullOrEmpty(uri))
{
try
{
using (var stream = Android.App.Application.Context.ContentResolver.OpenInputStream(Android.Net.Uri.Parse(uri)))
{
path = Path.Combine(Path.GetTempPath(), $"{ThumbnailPrefix}-{Guid.NewGuid()}");
using (var fstream = new FileStream(path, FileMode.Create))
{
stream.CopyTo(fstream);
fstream.Close();
}
stream.Close();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
var contact = new Contact
{
Name = GetString(cursor, ContactsContract.Contacts.InterfaceConsts.DisplayName),
Emails = emails,
Image = path,
PhoneNumbers = numbers,
};
return contact;
}
/// <summary>
/// 讀取聯繫人電話號碼
/// </summary>
/// <param name="ctx"></param>
/// <param name="contactId"></param>
/// <returns></returns>
string[] GetNumbers(Context ctx, string contactId)
{
var key = ContactsContract.CommonDataKinds.Phone.Number;
var cursor = ctx.ApplicationContext.ContentResolver.Query(
ContactsContract.CommonDataKinds.Phone.ContentUri,
null,
ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId + " = ?",
new[] { contactId },
null
);
return ReadCursorItems(cursor, key)?.ToArray();
}
/// <summary>
/// 讀取聯繫人郵箱地址
/// </summary>
/// <param name="ctx"></param>
/// <param name="contactId"></param>
/// <returns></returns>
string[] GetEmails(Context ctx, string contactId)
{
var key = ContactsContract.CommonDataKinds.Email.InterfaceConsts.Data;
var cursor = ctx.ApplicationContext.ContentResolver.Query(
ContactsContract.CommonDataKinds.Email.ContentUri,
null,
ContactsContract.CommonDataKinds.Email.InterfaceConsts.ContactId + " = ?",
new[] { contactId },
null);
return ReadCursorItems(cursor, key)?.ToArray();
}
IEnumerable<string> ReadCursorItems(ICursor cursor, string key)
{
while (cursor.MoveToNext())
{
var value = GetString(cursor, key);
yield return value;
}
cursor.Close();
}
string GetString(ICursor cursor, string key)
{
return cursor.GetString(cursor.GetColumnIndex(key));
}
}
}
需要添加 Plugin.CurrentActivity 和 Acr.UserDialogs 包。
7、Android工程添加許可權處理判斷類
Permission.Util
using Android.Content.PM;
namespace TerminalMACS.Clients.App.Droid
{
public static class PermissionUtil
{
/**
* 通過驗證給定數組中的每個條目的值是否為Permission.Granted,檢查是否已授予所有給定許可權。
*
* See Activity#onRequestPermissionsResult (int, String[], int[])
*/
public static bool VerifyPermissions(Permission[] grantResults)
{
// 必須至少檢查一個結果.
if (grantResults.Length < 1)
return false;
// 驗證是否已授予每個必需的許可權,否則返回false.
foreach (Permission result in grantResults)
{
if (result != Permission.Granted)
{
return false;
}
}
return true;
}
}
}
MainActivity.OnRequestPermissionResult是許可權申請結果處理函數,在此函數中調用ContactsService.OnRequestPermissionsResult通知通訊錄服務許可權處理結果。
MainActivity.cs
using Acr.UserDialogs;
using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using TerminalMACS.Clients.App.Droid.Services;
using TerminalMACS.Clients.App.Services;
namespace TerminalMACS.Clients.App.Droid
{
[Activity(Label = "TerminalMACS.Clients.App", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
IContactsService contactsService = new ContactsService();
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
UserDialogs.Init(() => this);
// 將通訊錄服務實例傳遞給共用庫,由共用庫使用讀取通訊錄介面
LoadApplication(new App(contactsService));
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
// 通訊錄服務處理許可權請求結果
ContactsService.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
8、創建通訊錄ViewModel,並使用通訊錄服務
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using TerminalMACS.Clients.App.Models;
using TerminalMACS.Clients.App.Services;
using Xamarin.Forms;
namespace TerminalMACS.Clients.App.ViewModels
{
/// <summary>
/// 通訊錄ViewModel
/// </summary>
public class ContactViewModel : BaseViewModel
{
/// <summary>
/// 通訊錄服務介面
/// </summary>
IContactsService _contactService;
/// <summary>
/// 標題
/// </summary>
public new string Title => "通訊錄";
private string _SearchText;
/// <summary>
/// 搜索關鍵字
/// </summary>
public string SearchText
{
get { return _SearchText; }
set
{
SetProperty(ref _SearchText, value);
}
}
/// <summary>
/// 通訊錄搜索命令
/// </summary>
public ICommand RaiseSearchCommand { get; }
/// <summary>
/// 通訊錄列表
/// </summary>
public ObservableCollection<Contact> Contacts { get; set; }
private List<Contact> _FilteredContacts;
/// <summary>
/// 通訊錄過濾列表
/// </summary>
public List<Contact> FilteredContacts
{
get { return _FilteredContacts; }
set
{
SetProperty(ref _FilteredContacts, value);
}
}
public ContactViewModel(IContactsService contactService)
{
_contactService = contactService;
Contacts = new ObservableCollection<Contact>();
Xamarin.Forms.BindingBase.EnableCollectionSynchronization(Contacts, null, ObservableCollectionCallback);
_contactService.OnContactLoaded += OnContactLoaded;
LoadContacts();
RaiseSearchCommand = new Command(RaiseSearchHandle);
}
/// <summary>
/// 過濾通訊錄
/// </summary>
void RaiseSearchHandle()
{
if (string.IsNullOrEmpty(SearchText))
{
FilteredContacts = Contacts.ToList();
return;
}
Func<Contact, bool> checkContact = (s) =>
{
if (!string.IsNullOrWhiteSpace(s.Name) && s.Name.ToLower().Contains(SearchText.ToLower()))
{
return true;
}
else if (s.PhoneNumbers.Length > 0 && s.PhoneNumbers.ToList().Exists(cu => cu.ToString().Contains(SearchText)))
{
return true;
}
return false;
};
FilteredContacts = Contacts.ToList().Where(checkContact).ToList();
}
/// <summary>
/// BindingBase.EnableCollectionSynchronization 為集合啟用跨線程更新
/// </summary>
/// <param name="collection"></param>
/// <param name="context"></param>
/// <param name="accessMethod"></param>
/// <param name="writeAccess"></param>
void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
{
// `lock` ensures that only one thread access the collection at a time
lock (collection)
{
accessMethod?.Invoke();
}
}
/// <summary>
/// 收到事件通知,讀取一條通訊錄信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnContactLoaded(object sender, ContactEventArgs e)
{
Contacts.Add(e.Contact);
RaiseSearchHandle();
}
/// <summary>
/// 非同步讀取終端通訊錄
/// </summary>
/// <returns></returns>
async Task LoadContacts()
{
try
{
await _contactService.RetrieveContactsAsync();
}
catch (TaskCanceledException)
{
Console.WriteLine("任務已經取消");
}
}
}
}
9、添加通訊錄頁面展示通訊錄數據
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
mc:Ignorable="d"
Title="{Binding Title}"
x:Class="TerminalMACS.Clients.App.Views.ContactPage"
ios:Page.UseSafeArea="true">
<ContentPage.Content>
<StackLayout>
<SearchBar x:Name="filterText"
HeightRequest="40"
Text="{Binding SearchText}"
SearchCommand="{Binding RaiseSearchCommand}"/>
<ListView ItemsSource="{Binding FilteredContacts}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10"
Orientation="Horizontal">
<Image Source="{Binding Image}"
VerticalOptions="Center"
x:Name="image"
Aspect="AspectFit"
HeightRequest="60"/>
<StackLayout VerticalOptions="Center">
<Label Text="{Binding Name}"
FontAttributes="Bold"/>
<Label Text="{Binding PhoneNumbers[0]}"/>
<Label Text="{Binding Emails[0]}"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
三、源碼獲取
-
2.Android客戶端可成功取得通訊錄數據,並可查詢;
已編譯的Android客戶端:https://terminalmacs.com/terminalmacs-clients-app-android
- 3.iOS讀取通訊錄功能代碼也已添加,但由於本人沒有iOS測試環境,所以未驗證,有條件的朋友可以測試下iOS的通訊錄讀取功能,如果代碼不起作用,可參考本文參考的文章檢查iOS代碼。
四、參考資料
Getting phone contacts in Xamarin Forms:https://www.xamboy.com/2019/10/10/getting-phone-contacts-in-xamarin-forms/
參考文章末尾有源代碼鏈接。
五、後面計劃
Xamarin.Forms客戶端基本信息獲取,比如IMEI、IMSI、本機號碼、Mac地址等。