1. 功能简介

1.1. 读取手机基本信息

主要使用Xamarin.Essentials库获取设备基本信息,Xam.Plugin.DeviceInfo插件获取App Id,其实该插件也能获取设备基本信息。

1.2. 读取手机联系人信息

Android和iOS工程具体实现联系人读取服务,使用到DependencyService获取服务功能。

1.3. 应用本地化

使用资源文件实现本地化,目前只做了中、英文。

2. 详细功能说明

2.1. 读取手机基本信息

Xamarin.Essentials库用于获取手机基本信息,比如手机厂商、型号、名称、类型、版本等;Xam.Plugin.DeviceInfo插件获取App Id,用于唯一标识不同手机,获取信息见下图:

代码结构如下图:

ClientInfoViewModel.cs

  1. using Plugin.DeviceInfo;

  2. using System;

  3. using Xamarin.Essentials;

  4. namespace TerminalMACS.Clients.App.ViewModels

  5. {

  6. /// <summary>

  7. /// Client base information page ViewModel

  8. /// </summary>

  9. public class ClientInfoViewModel : BaseViewModel

  10. {

  11. /// <summary>

  12. /// Gets or sets the id of the application.

  13. /// </summary>

  14. public string AppId { get; set; } = CrossDeviceInfo.Current.GenerateAppId();

  15. /// <summary>

  16. /// Gets or sets the model of the device.

  17. /// </summary>

  18. public string Model { get; private set; } = DeviceInfo.Model;

  19. /// <summary>

  20. /// Gets or sets the manufacturer of the device.

  21. /// </summary>

  22. public string Manufacturer { get; private set; } = DeviceInfo.Manufacturer;

  23. /// <summary>

  24. /// Gets or sets the name of the device.

  25. /// </summary>

  26. public string Name { get; private set; } = DeviceInfo.Name;

  27. /// <summary>

  28. /// Gets or sets the version of the operating system.

  29. /// </summary>

  30. public string VersionString { get; private set; } = DeviceInfo.VersionString;

  31. /// <summary>

  32. /// Gets or sets the version of the operating system.

  33. /// </summary>

  34. public Version Version { get; private set; } = DeviceInfo.Version;

  35. /// <summary>

  36. /// Gets or sets the platform or operating system of the device.

  37. /// </summary>

  38. public DevicePlatform Platform { get; private set; } = DeviceInfo.Platform;

  39. /// <summary>

  40. /// Gets or sets the idiom of the device.

  41. /// </summary>

  42. public DeviceIdiom Idiom { get; private set; } = DeviceInfo.Idiom;

  43. /// <summary>

  44. /// Gets or sets the type of device the application is running on.

  45. /// </summary>

  46. public DeviceType DeviceType { get; private set; } = DeviceInfo.DeviceType;

  47. }

  48. }

ClientInfoPage.xaml

  1. <?xml version="1.0" encoding="utf-8" ?>

  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  5. xmlns:d="http://xamarin.com/schemas/2014/forms/design"

  6. xmlns:resources="clr-namespace:TerminalMACS.Clients.App.Resx"

  7. xmlns:vm="clr-namespace:TerminalMACS.Clients.App.ViewModels"

  8. mc:Ignorable="d"

  9. x:Class="TerminalMACS.Clients.App.Views.ClientInfoPage"

  10. Title="{x:Static resources:AppResource.Title_ClientInfoPage}">

  11. <ContentPage.BindingContext>

  12. <vm:ClientInfoViewModel/>

  13. </ContentPage.BindingContext>

  14. <ContentPage.Content>

  15. <StackLayout>

  16. <Label Text="{x:Static resources:AppResource.AppId}"/>

  17. <Label Text="{Binding AppId}" FontAttributes="Bold" Margin="10,0,0,10"/>

  18. <Label Text="{x:Static resources:AppResource.DeviceModel}"/>

  19. <Label Text="{Binding Model}" FontAttributes="Bold" Margin="10,0,0,10"/>

  20. <Label Text="{x:Static resources:AppResource.DeviceManufacturer}"/>

  21. <Label Text="{Binding Manufacturer}" FontAttributes="Bold" Margin="10,0,0,10"/>

  22. <Label Text="{x:Static resources:AppResource.DeviceName}"/>

  23. <Label Text="{Binding Name}" FontAttributes="Bold" Margin="10,0,0,10"/>

  24. <Label Text="{x:Static resources:AppResource.DeviceVersionString}"/>

  25. <Label Text="{Binding VersionString}" FontAttributes="Bold" Margin="10,0,0,10"/>

  26. <Label Text="{x:Static resources:AppResource.DevicePlatform}"/>

  27. <Label Text="{Binding Platform}" FontAttributes="Bold" Margin="10,0,0,10"/>

  28. <Label Text="{x:Static resources:AppResource.DeviceIdiom}"/>

  29. <Label Text="{Binding Idiom}" FontAttributes="Bold" Margin="10,0,0,10"/>

  30. <Label Text="{x:Static resources:AppResource.DeviceType}"/>

  31. <Label Text="{Binding DeviceType}" FontAttributes="Bold" Margin="10,0,0,10"/>

  32. </StackLayout>

  33. </ContentPage.Content>

  34. </ContentPage>

2.2. 读取手机联系人信息

Android和iOS工程具体实现联系人读取服务,使用到DependencyService获取服务功能,功能截图如下:

2.2.1. TerminalMACS.Clients.App

代码结构如下图:

2.2.1.1. 联系人实体类:Contacts.cs

目前只获取联系人名称、图片、电子邮件(可能多个)、电话号码(可能多个),更多可以扩展。

  1. namespace TerminalMACS.Clients.App.Models

  2. {

  3. /// <summary>

  4. /// Contact information entity.

  5. /// </summary>

  6. public class Contact

  7. {

  8. /// <summary>

  9. /// Gets or sets the name

  10. /// </summary>

  11. public string Name { get; set; }

  12. /// <summary>

  13. /// Gets or sets the image

  14. /// </summary>

  15. public string Image { get; set; }

  16. /// <summary>

  17. /// Gets or sets the emails

  18. /// </summary>

  19. public string[] Emails { get; set; }

  20. /// <summary>

  21. /// Gets or sets the phone numbers

  22. /// </summary>

  23. public string[] PhoneNumbers { get; set; }

  24. }

  25. }

2.2.1.2. 联系人服务接口:IContactsService.cs

包括:

  • 一个联系人获取请求接口:RetrieveContactsAsync

  • 一个读取一条联系人结果通知事件:OnContactLoaded

该接口由具体平台(Android和iOS)实现。

  1. using System;

  2. using System.Collections.Generic;

  3. using System.Threading;

  4. using System.Threading.Tasks;

  5. using TerminalMACS.Clients.App.Models;

  6. namespace TerminalMACS.Clients.App.Services

  7. {

  8. /// <summary>

  9. /// Read a contact record notification event parameter.

  10. /// </summary>

  11. public class ContactEventArgs:EventArgs

  12. {

  13. public Contact Contact { get; }

  14. public ContactEventArgs(Contact contact)

  15. {

  16. Contact = contact;

  17. }

  18. }

  19. /// <summary>

  20. /// Contact service interface, which is required for Android and iOS terminal specific

  21. ///  contact acquisition service needs to implement this interface.

  22. /// </summary>

  23. public interface IContactsService

  24. {

  25. /// <summary>

  26. /// Read a contact record and notify the shared library through this event.

  27. /// </summary>

  28. event EventHandler<ContactEventArgs> OnContactLoaded;

  29. /// <summary>

  30. /// Loading or not

  31. /// </summary>

  32. bool IsLoading { get; }

  33. /// <summary>

  34. /// Try to get all contact information

  35. /// </summary>

  36. /// <param name="token"></param>

  37. /// <returns></returns>

  38. Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? token = null);

  39. }

  40. }

2.2.1.3. 联系人VM:ContactViewModel.cs

VM提供下面两个功能:

  1. 全部联系人加载。

  2. 联系人关键字查询。

  1. using System;

  2. using System.Collections;

  3. using System.Collections.Generic;

  4. using System.Collections.ObjectModel;

  5. using System.Linq;

  6. using System.Threading.Tasks;

  7. using System.Windows.Input;

  8. using TerminalMACS.Clients.App.Models;

  9. using TerminalMACS.Clients.App.Resx;

  10. using TerminalMACS.Clients.App.Services;

  11. using Xamarin.Forms;

  12. namespace TerminalMACS.Clients.App.ViewModels

  13. {

  14. /// <summary>

  15. /// Contact page ViewModel

  16. /// </summary>

  17. public class ContactViewModel : BaseViewModel

  18. {

  19. /// <summary>

  20. /// Contact service interface

  21. /// </summary>

  22. IContactsService _contactService;

  23. private string _SearchText;

  24. /// <summary>

  25. /// Gets or sets the search text of the contact list.

  26. /// </summary>

  27. public string SearchText

  28. {

  29. get { return _SearchText; }

  30. set

  31. {

  32. SetProperty(ref _SearchText, value);

  33. }

  34. }

  35. /// <summary>

  36. /// The search contact command.

  37. /// </summary>

  38. public ICommand RaiseSearchCommand { get; }

  39. /// <summary>

  40. /// The contact list.

  41. /// </summary>

  42. public ObservableCollection<Contact> Contacts { get; set; }

  43. private List<Contact> _FilteredContacts;

  44. /// <summary>

  45. /// Contact filter list.

  46. /// </summary>

  47. public List<Contact> FilteredContacts

  48. {

  49. get { return _FilteredContacts; }

  50. set

  51. {

  52. SetProperty(ref _FilteredContacts, value);

  53. }

  54. }

  55. public ContactViewModel()

  56. {

  57. _contactService = DependencyService.Get<IContactsService>();

  58. Contacts = new ObservableCollection<Contact>();

  59. Xamarin.Forms.BindingBase.EnableCollectionSynchronization(Contacts, null, ObservableCollectionCallback);

  60. _contactService.OnContactLoaded += OnContactLoaded;

  61. LoadContacts();

  62. RaiseSearchCommand = new Command(RaiseSearchHandle);

  63. }

  64. /// <summary>

  65. /// Filter contact list

  66. /// </summary>

  67. void RaiseSearchHandle()

  68. {

  69. if (string.IsNullOrEmpty(SearchText))

  70. {

  71. FilteredContacts = Contacts.ToList();

  72. return;

  73. }

  74. Func<Contact, bool> checkContact = (s) =>

  75. {

  76. if (!string.IsNullOrWhiteSpace(s.Name) && s.Name.ToLower().Contains(SearchText.ToLower()))

  77. {

  78. return true;

  79. }

  80. else if (s.PhoneNumbers.Length > 0 && s.PhoneNumbers.ToList().Exists(cu => cu.ToString().Contains(SearchText)))

  81. {

  82. return true;

  83. }

  84. return false;

  85. };

  86. FilteredContacts = Contacts.ToList().Where(checkContact).ToList();

  87. }

  88. /// <summary>

  89. /// BindingBase.EnableCollectionSynchronization

  90. ///     Enable cross thread updates for collections

  91. /// </summary>

  92. /// <param name="collection"></param>

  93. /// <param name="context"></param>

  94. /// <param name="accessMethod"></param>

  95. /// <param name="writeAccess"></param>

  96. void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)

  97. {

  98. // `lock` ensures that only one thread access the collection at a time

  99. lock (collection)

  100. {

  101. accessMethod?.Invoke();

  102. }

  103. }

  104. /// <summary>

  105. /// Received a event notification that a contact information was successfully read.

  106. /// </summary>

  107. /// <param name="sender"></param>

  108. /// <param name="e"></param>

  109. private void OnContactLoaded(object sender, ContactEventArgs e)

  110. {

  111. Contacts.Add(e.Contact);

  112. RaiseSearchHandle();

  113. }

  114. /// <summary>

  115. /// Read contact information asynchronously

  116. /// </summary>

  117. /// <returns></returns>

  118. async Task LoadContacts()

  119. {

  120. try

  121. {

  122. await _contactService.RetrieveContactsAsync();

  123. }

  124. catch (TaskCanceledException)

  125. {

  126. Console.WriteLine(AppResource.TaskCancelled);

  127. }

  128. }

  129. }

  130. }

2.2.1.4. 联系人展示页面:ContactPage.xaml

简单的布局,一个StackLayout布局容器竖直排列,一个SearchBar提供关键字搜索功能。

  1. <?xml version="1.0" encoding="utf-8" ?>

  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  5. xmlns:d="http://xamarin.com/schemas/2014/forms/design"

  6. xmlns:resources="clr-namespace:TerminalMACS.Clients.App.Resx"

  7. xmlns:vm="clr-namespace:TerminalMACS.Clients.App.ViewModels"

  8. xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"

  9. mc:Ignorable="d"

  10. Title="{x:Static resources:AppResource.Title_ContactPage}"

  11. x:Class="TerminalMACS.Clients.App.Views.ContactPage"

  12. ios:Page.UseSafeArea="true">

  13. <ContentPage.BindingContext>

  14. <vm:ContactViewModel/>

  15. </ContentPage.BindingContext>

  16. <ContentPage.Content>

  17. <StackLayout>

  18. <SearchBar x:Name="filterText"

  19. HeightRequest="40"

  20. Text="{Binding SearchText}"

  21. SearchCommand="{Binding RaiseSearchCommand}"/>

  22. <ListView   ItemsSource="{Binding FilteredContacts}"

  23. HasUnevenRows="True">

  24. <ListView.ItemTemplate>

  25. <DataTemplate>

  26. <ViewCell>

  27. <StackLayout Padding="10"

  28. Orientation="Horizontal">

  29. <Image  Source="{Binding Image}"

  30. VerticalOptions="Center"

  31. x:Name="image"

  32. Aspect="AspectFit"

  33. HeightRequest="60"/>

  34. <StackLayout VerticalOptions="Center">

  35. <Label Text="{Binding Name}"

  36. FontAttributes="Bold"/>

  37. <Label Text="{Binding PhoneNumbers[0]}"/>

  38. <Label Text="{Binding Emails[0]}"/>

  39. </StackLayout>

  40. </StackLayout>

  41. </ViewCell>

  42. </DataTemplate>

  43. </ListView.ItemTemplate>

  44. </ListView>

  45. </StackLayout>

  46. </ContentPage.Content>

  47. </ContentPage>

2.2.2. Android

代码结构如下图:

  • AndroidManifest.xml:写入读、写联系人权限请求。

  • ContactsService.cs:具体的联系人权限请求、数据读取操作。

  • MainActivity.cs:接收权限请求结果

  • MainApplicaion.cs:此类未添加任务关键代码,但必不可少,否则无法正确弹出权限请求窗口。

  • PermissionUtil.cs:权限请求结果判断

2.2.2.1. AndroidManifest.xml添加权限

只添加下面这一行即可:

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

2.2.2.2. ContactsService.cs

Android联系人获取实现服务,实现IContactsService。注意命名空间上的特性代码,必须添加上这个特性后,在前面的联系人VM中才能使用DependencyService.Get()获取此服务实例,默认服务是单例的:

  1. [assembly: Xamarin.Forms.Dependency(typeof(TerminalMACS.Clients.App.iOS.Services.ContactsService))]

  2. using Contacts;

  3. using Foundation;

  4. using System;

  5. using System.Collections.Generic;

  6. using System.IO;

  7. using System.Linq;

  8. using System.Threading;

  9. using System.Threading.Tasks;

  10. using TerminalMACS.Clients.App.Models;

  11. using TerminalMACS.Clients.App.Services;

  12. [assembly: Xamarin.Forms.Dependency(typeof(TerminalMACS.Clients.App.iOS.Services.ContactsService))]

  13. namespace TerminalMACS.Clients.App.iOS.Services

  14. {

  15. /// <summary>

  16. /// Contact service.

  17. /// </summary>

  18. public class ContactsService : NSObject, IContactsService

  19. {

  20. const string ThumbnailPrefix = "thumb";

  21. bool requestStop = false;

  22. public event EventHandler<ContactEventArgs> OnContactLoaded;

  23. bool _isLoading = false;

  24. public bool IsLoading => _isLoading;

  25. /// <summary>

  26. /// Asynchronous request permission

  27. /// </summary>

  28. /// <returns></returns>

  29. public async Task<bool> RequestPermissionAsync()

  30. {

  31. var status = CNContactStore.GetAuthorizationStatus(CNEntityType.Contacts);

  32. Tuple<bool, NSError> authotization = new Tuple<bool, NSError>(status == CNAuthorizationStatus.Authorized, null);

  33. if (status == CNAuthorizationStatus.NotDetermined)

  34. {

  35. using (var store = new CNContactStore())

  36. {

  37. authotization = await store.RequestAccessAsync(CNEntityType.Contacts);

  38. }

  39. }

  40. return authotization.Item1;

  41. }

  42. /// <summary>

  43. /// Request contact asynchronously. This method is called by the interface.

  44. /// </summary>

  45. /// <param name="cancelToken"></param>

  46. /// <returns></returns>

  47. public async Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? cancelToken = null)

  48. {

  49. requestStop = false;

  50. if (!cancelToken.HasValue)

  51. cancelToken = CancellationToken.None;

  52. // We create a TaskCompletionSource of decimal

  53. var taskCompletionSource = new TaskCompletionSource<IList<Contact>>();

  54. // Registering a lambda into the cancellationToken

  55. cancelToken.Value.Register(() =>

  56. {

  57. // We received a cancellation message, cancel the TaskCompletionSource.Task

  58. requestStop = true;

  59. taskCompletionSource.TrySetCanceled();

  60. });

  61. _isLoading = true;

  62. var task = LoadContactsAsync();

  63. // Wait for the first task to finish among the two

  64. var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);

  65. _isLoading = false;

  66. return await completedTask;

  67. }

  68. /// <summary>

  69. /// Load contacts asynchronously, fact reading method of address book.

  70. /// </summary>

  71. /// <returns></returns>

  72. async Task<IList<Contact>> LoadContactsAsync()

  73. {

  74. IList<Contact> contacts = new List<Contact>();

  75. var hasPermission = await RequestPermissionAsync();

  76. if (hasPermission)

  77. {

  78. NSError error = null;

  79. var keysToFetch = new[] { CNContactKey.PhoneNumbers, CNContactKey.GivenName, CNContactKey.FamilyName, CNContactKey.EmailAddresses, CNContactKey.ImageDataAvailable, CNContactKey.ThumbnailImageData };

  80. var request = new CNContactFetchRequest(keysToFetch: keysToFetch);

  81. request.SortOrder = CNContactSortOrder.GivenName;

  82. using (var store = new CNContactStore())

  83. {

  84. var result = store.EnumerateContacts(request, out error, new CNContactStoreListContactsHandler((CNContact c, ref bool stop) =>

  85. {

  86. string path = null;

  87. if (c.ImageDataAvailable)

  88. {

  89. path = path = Path.Combine(Path.GetTempPath(), $"{ThumbnailPrefix}-{Guid.NewGuid()}");

  90. if (!File.Exists(path))

  91. {

  92. var imageData = c.ThumbnailImageData;

  93. imageData?.Save(path, true);

  94. }

  95. }

  96. var contact = new Contact()

  97. {

  98. Name = string.IsNullOrEmpty(c.FamilyName) ? c.GivenName : $"{c.GivenName} {c.FamilyName}",

  99. Image = path,

  100. PhoneNumbers = c.PhoneNumbers?.Select(p => p?.Value?.StringValue).ToArray(),

  101. Emails = c.EmailAddresses?.Select(p => p?.Value?.ToString()).ToArray(),

  102. };

  103. if (!string.IsNullOrWhiteSpace(contact.Name))

  104. {

  105. OnContactLoaded?.Invoke(this, new ContactEventArgs(contact));

  106. contacts.Add(contact);

  107. }

  108. stop = requestStop;

  109. }));

  110. }

  111. }

  112. return contacts;

  113. }

  114. }

  115. }

2.2.2.3. MainActivity.cs

代码简单,只在OnRequestPermissionsResult方法中接收权限请求结果:

  1. // The contact service processes the result of the permission request.

  2. ContactsService.OnRequestPermissionsResult(requestCode, permissions, grantResults);

2.2.3. iOS

代码结构如下图:

  • ContactsService.cs:具体的联系人权限请求、数据读取操作。

  • Info.plist:权限请求时描述文件

2.2.3.1. ContactsService.cs

iOS具体的联系人读取服务,实现IContactsService接口,原理同Android联系人服务类似,本人无调试环境,iOS此功能未测试。

  1. using Contacts;

  2. using Foundation;

  3. using System;

  4. using System.Collections.Generic;

  5. using System.IO;

  6. using System.Linq;

  7. using System.Threading;

  8. using System.Threading.Tasks;

  9. using TerminalMACS.Clients.App.Models;

  10. using TerminalMACS.Clients.App.Services;

  11. [assembly: Xamarin.Forms.Dependency(typeof(TerminalMACS.Clients.App.iOS.Services.ContactsService))]

  12. namespace TerminalMACS.Clients.App.iOS.Services

  13. {

  14. /// <summary>

  15. /// Contact service.

  16. /// </summary>

  17. public class ContactsService : NSObject, IContactsService

  18. {

  19. const string ThumbnailPrefix = "thumb";

  20. bool requestStop = false;

  21. public event EventHandler<ContactEventArgs> OnContactLoaded;

  22. bool _isLoading = false;

  23. public bool IsLoading => _isLoading;

  24. /// <summary>

  25. /// Asynchronous request permission

  26. /// </summary>

  27. /// <returns></returns>

  28. public async Task<bool> RequestPermissionAsync()

  29. {

  30. var status = CNContactStore.GetAuthorizationStatus(CNEntityType.Contacts);

  31. Tuple<bool, NSError> authotization = new Tuple<bool, NSError>(status == CNAuthorizationStatus.Authorized, null);

  32. if (status == CNAuthorizationStatus.NotDetermined)

  33. {

  34. using (var store = new CNContactStore())

  35. {

  36. authotization = await store.RequestAccessAsync(CNEntityType.Contacts);

  37. }

  38. }

  39. return authotization.Item1;

  40. }

  41. /// <summary>

  42. /// Request contact asynchronously. This method is called by the interface.

  43. /// </summary>

  44. /// <param name="cancelToken"></param>

  45. /// <returns></returns>

  46. public async Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? cancelToken = null)

  47. {

  48. requestStop = false;

  49. if (!cancelToken.HasValue)

  50. cancelToken = CancellationToken.None;

  51. // We create a TaskCompletionSource of decimal

  52. var taskCompletionSource = new TaskCompletionSource<IList<Contact>>();

  53. // Registering a lambda into the cancellationToken

  54. cancelToken.Value.Register(() =>

  55. {

  56. // We received a cancellation message, cancel the TaskCompletionSource.Task

  57. requestStop = true;

  58. taskCompletionSource.TrySetCanceled();

  59. });

  60. _isLoading = true;

  61. var task = LoadContactsAsync();

  62. // Wait for the first task to finish among the two

  63. var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);

  64. _isLoading = false;

  65. return await completedTask;

  66. }

  67. /// <summary>

  68. /// Load contacts asynchronously, fact reading method of address book.

  69. /// </summary>

  70. /// <returns></returns>

  71. async Task<IList<Contact>> LoadContactsAsync()

  72. {

  73. IList<Contact> contacts = new List<Contact>();

  74. var hasPermission = await RequestPermissionAsync();

  75. if (hasPermission)

  76. {

  77. NSError error = null;

  78. var keysToFetch = new[] { CNContactKey.PhoneNumbers, CNContactKey.GivenName, CNContactKey.FamilyName, CNContactKey.EmailAddresses, CNContactKey.ImageDataAvailable, CNContactKey.ThumbnailImageData };

  79. var request = new CNContactFetchRequest(keysToFetch: keysToFetch);

  80. request.SortOrder = CNContactSortOrder.GivenName;

  81. using (var store = new CNContactStore())

  82. {

  83. var result = store.EnumerateContacts(request, out error, new CNContactStoreListContactsHandler((CNContact c, ref bool stop) =>

  84. {

  85. string path = null;

  86. if (c.ImageDataAvailable)

  87. {

  88. path = path = Path.Combine(Path.GetTempPath(), $"{ThumbnailPrefix}-{Guid.NewGuid()}");

  89. if (!File.Exists(path))

  90. {

  91. var imageData = c.ThumbnailImageData;

  92. imageData?.Save(path, true);

  93. }

  94. }

  95. var contact = new Contact()

  96. {

  97. Name = string.IsNullOrEmpty(c.FamilyName) ? c.GivenName : $"{c.GivenName} {c.FamilyName}",

  98. Image = path,

  99. PhoneNumbers = c.PhoneNumbers?.Select(p => p?.Value?.StringValue).ToArray(),

  100. Emails = c.EmailAddresses?.Select(p => p?.Value?.ToString()).ToArray(),

  101. };

  102. if (!string.IsNullOrWhiteSpace(contact.Name))

  103. {

  104. OnContactLoaded?.Invoke(this, new ContactEventArgs(contact));

  105. contacts.Add(contact);

  106. }

  107. stop = requestStop;

  108. }));

  109. }

  110. }

  111. return contacts;

  112. }

  113. }

  114. }

2.2.3.2. Info.plist

联系人权限请求说明

2.3. 应用本地化

使用资源文件实现本地化,目前只做了中、英文。

资源文件如下:

指定默认区域性

要使资源文件可正常使用,应用程序必须指定 NeutralResourcesLanguage。在共享项目中,应自定义 AssemblyInfo.cs 文件以指定默认区域性 。以下代码演示如何在 AssemblyInfo.cs 文件中将 NeutralResourcesLanguage 设置为 zh-CN (摘自官方文档:https://docs.microsoft.com/zh-cn/samples/xamarin/xamarin-forms-samples/usingresxlocalization/,后经测试,注释下面这段代码也能正常本地化):

  1. [assembly: NeutralResourcesLanguage("zh-Hans")]

XAML中使用

引入资源文件命名空间

  1. xmlns:resources="clr-namespace:TerminalMACS.Clients.App.Resx"

具体使用如

  1. <Label Text="{x:Static resources:AppResource.ClientName_AboutPage}" FontAttributes="Bold"/>

3. 关于TerminalMACS及本客户端

3.1. TermainMACS

多终端资源管理与检测系统,包含多个子进程模块,目前只开发了Xamarin.Forms客户端,下一步开发服务端,使用 .NET 5 Web API开发,基于Abp vNext搭建。

3.2. Xamarin.Forms客户端

作为TerminalMACS系统的一个子进程模块,目前只开发了手机基本信息获取、联系人信息获取、本地化功能,后续开发服务端时,会配合添加通信功能,比如连接服务端验证、主动推送已获取资源等。

3.3. 关于项目开源

  1. 开源项目地址:https://github.com/dotnet9/TerminalMACS

除非注明,文章均由 TerminalMACS 整理发布,欢迎转载。

转载请注明本文地址:https://terminalmacs.com/890.html

Xamarin.Forms客户端第一版相关推荐

  1. Xamarin.Forms读取并展示Android和iOS通讯录 - TerminalMACS客户端

    本文同步更新地址: https://dotnet9.com/11520.html https://terminalmacs.com/861.html 阅读导航: 一.功能说明 二.代码实现 三.源码获 ...

  2. Xamarin.Forms 5.0 项目实战发布!

    活动介绍 本次活动主要是 .NET Xamarin.Forms 移动端项目开发实战教程, 与以往相同, 本次的收入(其它部分会另行说明) 将用于社区公益活动, 不限于: 公益性质的个人/组织机构捐赠 ...

  3. .NET Standard@Xamarin.Forms

    编者语 : .NET技术社群由于微软的开源和跨平台性越来越受关注,但国内都是碍于大家以往的认识阻碍了发展,没有太多大的企业敢于去把.NET作为第一生产力.你作为.NET程序员有义务去保护好你的饭碗,也 ...

  4. 使用Xamarin.Forms的企业应用程序模式(电子书)--访问远程数据

    许多现代的基于Web的解决方案利用由Web服务器托管的Web服务来为远程客户端应用程序提供功能. Web服务公开的操作构成Web API. 客户端应用程序应该能够在不知道API暴露的数据或操作如何实现 ...

  5. Xamarin.Forms 简介

    An Introduction to Xamarin.Forms 来源:http://developer.xamarin.com/guides/cross-platform/xamarin-forms ...

  6. Xamarin.Forms 之我的花园 - 2.从照片库选择图片和拍照功能

    文章目录 1.介绍 2.预览 3.调试方式对比 4.MediaPlugin 5.权限设置 5.1 iOS权限设置 5.2 Android权限设置 1.修改MainActivity.cs 2.File ...

  7. Syncfusion教程:在Xamarin.Forms中创建数据输入表单 (3)

    2019独角兽企业重金招聘Python工程师标准>>> 下载Essential Studio for Xamarin最新版本 Essential Studio for Xamarin ...

  8. Xamarin.Forms探索--使用 Xamarin.Forms 来创建跨平台的用户界面

    Xamarin.Forms 是一个跨平台的.基于原生控件的UI工具包,开发人员可以轻松的创建适用于 Android,iOS 以及 Windows Phone的用户界面.与HTML 5 不同的时,Xam ...

  9. Xamarin.Forms Shell基础教程(1)

    Xamarin.Forms Shell基础教程(1) 什么是Xamarin.Forms Shell Shell是Visual Studio为Xamarin Forms提供的解决方案模版.本质上,She ...

最新文章

  1. python使用教程pandas-「Python」pandas入门教程
  2. 存储管理算法java代码
  3. java 双工模式_rabbitMq-工作模式(双工模式)-java
  4. POJ 3159 Candies
  5. 细说ASP.NET Forms身份认证
  6. #上传文件的post请求
  7. 87.http upstream模块提供的变量
  8. Q:How to read attribute from a tag
  9. 发电厂计算机控制系统,火电厂dcs控制系统
  10. 数据结构:二叉树的非递归遍历
  11. XCTF-Cat+Bug
  12. PCB的安装与元器件的绘制
  13. 逻辑回归 自由度_回归自由度的官方定义
  14. Java基础知识(十) 多线程
  15. 多懂点SQL可以写出更好的接口
  16. 云计算机服务层次,云计算包括哪几个层次的服务?
  17. 电脑锁定屏幕网络就断开的解决方案
  18. 画图解释FHSS、DSSS扩频原理以及计算规则
  19. 牛津大学出品:随机噪声对抗训练
  20. hp服务器硬盘检测,检查HP服务器硬盘状态脚本

热门文章

  1. 【转】Linux中断处理学习笔记
  2. httpModules 与 httpHandlers
  3. php Collection类的设计
  4. php speedtest,大神教你如何搭建自己的web speedtest站点
  5. matlab怎么画一箭穿心,MATLAB学习与使用:如何绘制三维心形图 经验告诉你该这样...
  6. Ubuntu Core 给物联网提供更多安全支持
  7. JavaScript 学习提升
  8. js 深复制一个对象
  9. zabbix邮件发不出去
  10. ADO.NET笔记——基本概念