Xamarin.Forms客户端第一版

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.            

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

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

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

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

  23.            

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

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

  26.            

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

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

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

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

  31.            

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

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

  34.            

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

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

  37.        </StackLayout>

  38.    </ContentPage.Content>

  39. </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

相关说明: ConsoleApplication1_sERVER ----PC-服务端 MyNetTest --------------------IOS-客户端 1、PC-服务端 只是一个运行在windows系统下的 控制台程序。接收来自客户端的信息。 2、IOS-客户端 运行在ios模拟器上,连接PC服务端的ip,发生相关信息。 3、两台主机,一台是运行windows系统的计算机。另外一台是MacBook计算机。运行ios模拟器。 4、pc-服务端,可以用vs2008打开并且编辑。 5、ios客户端,使用的是XamarinStudio 打开并且编辑。 以下是这个例子中的特别提到的地方 A、这个例子是完全用C#写的。 B、ios由于是伪后台,当程序退回到后台,系统留给程序的可运行时间就只有3分钟。 过了3分钟,就会把这个程序的所有线程挂起(当然内部预留了长任务运行这一后招)。 经过多次试验后,可以借助着3分钟的长任务运行,然后通过简单的修改来突破这个界限。 在本例子中,所有线程共享一个线程ID。 其中只需要一条线程负责不停的延长这个线程ID的运行时间,然后其他的线程就只需要专注于其本应该要做的任务即可。 C、本例子中,还实现了另外一个功能,就是ios程序与PC程序进行socket。当然是最为简单的。 ---------------- 由于本人也是刚刚使用c#开发ios程序,初入门,为了这两个问题,也是研究了很久,可查的资料又很少。 希望能够帮助到同样遇到困难的你。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值