Windows 10 UWP 8 of N: Contact, appointments, message, user`s data
早在Windows Phone的時候很多APP就已經跟People hub (人員),藉由連結各個Contact provider的APP連結成一個聯絡人資訊。
主要跟user data非常相關的有以下幾個功能
appointment (行事曆)
calls(撥號)
contacts(聯絡人)
Email(電子信箱)
Text messages(簡訊)
User data accounts(使用者帳號[ Aka 新增帳戶 ])
在抓取這些資訊時必須先在Manifest[ Package.appmanifest檔案 ]做宣告。這邊不能用內建的UI介面做調整,需要使用XML開啟並在capabilities加入以下權限。
<Capabilities>
    <Capability Name="internetClient" />
    <uap:Capability Name="contacts"/>
</Capabilities>
預設情形會加入internetClient,需手動加入的是contacts。
接者來寫Code存取聯絡人資訊!
public async void GetContact(ContactStoreAccessType accessType)
{
	var contactStore = await ContactManager.RequestStoreAsync(accessType);
        if (contactStore == null)
        {
        return null;
        }
        var reader = contactStore.GetContactReader();
        var readerBatch = await reader.ReadBatchAsync();
        var contactList = new List<Contact>();
		while (readerBatch.Status == ContactBatchStatus.Success && readerBatch.Contacts.Count != 0)
		{
			foreach (var contact in readerBatch.Contacts)
			{
				contactList.Add(contact);
			}
			readerBatch = await reader.ReadBatchAsync();
		}
	return contactList;
}
這邊有幾點必須注意在抓取contact的資料時帶入存取的權限enum
public enum ContactStoreAccessType
    {
        //
        // Summary:
        //     Read and write contacts that belong to the app only.
        AppContactsReadWrite = 0,
        //
        // Summary:
        //     Read access to all app and system contacts.
        AllContactsReadOnly = 1,
        //
        // Summary:
        //     Read and write access to all app and system contacts.
        AllContactsReadWrite = 2
    }
存取權限可以分為->讀寫、唯讀、所以系統註冊之聯絡人讀寫。所有系統註冊就是包含其他開發人員所提供contact store!
接者是如果抓取失敗的時候會丟回null值。
讀取聯絡人方式需要用ContactReader來做讀取的物件並且讀取的方式是採用批次[Batch]的方式做讀取。
讀取成功後抓取聯絡人資料到ListView的畫面如下
PS:由於抓取我個人的Contact所以將上籃線遮蔽
接下來是建立自己的APP的Contact Store
public async Task CreateContactStoreAsync()
{
	var success = await TryOpenContactStoreAsync();
    if (!success)
    	return;
    var contactLists = await store.FindContactListsAsync();
    if (contactLists.Count != 0)
    {
        System.Diagnostics.Debug.WriteLine("Contact List exit");
    	return;
    }
    var simpleContactList = await store.CreateContactListAsync(contactListId);
    simpleContactList.IsHidden = false;
    simpleContactList.OtherAppReadAccess = ContactListOtherAppReadAccess.Full;
    simpleContactList.OtherAppWriteAccess = ContactListOtherAppWriteAccess.SystemOnly;
	await simpleContactList.SaveAsync();
}
這邊建立的方式需要先抓取系統的ContactStore並建立ContactList!
ContactList有以下幾點重要的API
Read-Only
| 屬性 | 存取層級 | 敘述 | 
| ChangeTracker | Read-only | 取得該ContactList的ContactChangeTracker | 
| DisplayName | Read/Write | 顯示名稱 | 
| Id | Read-Only | 取得該ContactList的識別碼 | 
| IsHidden | Read/Write | 是否顯示ContactList在UI上 | 
| OtherAppReadAccess | Read/Write | 設定其他APP讀取該APP的權限 | 
| OtherAppWriteAccess | Read/Write | 設定其他APP寫入該APP的權限 | 
| SourceDisplayName | Read-Only | ContactList的提供來源字串 | 
| SupportsServerSearch | Read-Only | 是否能搜尋遠端伺服器 | 
| SyncManager | Read-Only | 與伺服器溝通物件 | 
| UserDataAccountId | Read-Only | 取得建立該contactlist的使用者帳號ID | 
這邊會有個非常強大的功能ChangeTracker!沒錯就像是版本控制相似的功能,可以抓取聯絡人資訊變更紀錄。在文章下半段會詳細介紹該API使用。
基本上調整IsHidden、OtherAppReadAccess和OtherAppWriteAccess就可以建立基本的ContactList了!
這邊關於OtherAppWriteAccess的權限要特別注意,因為這會取決於是否能夠藉由系統的APP來刪除以及修改聯絡人資訊。
以下程式碼來新增聯絡人
public async void InserNewContact()
{
    var contactInfo = new Contact();
    contactInfo.FirstName = "EW";
    contactInfo.LastName = "Taicho";
    contactInfo.Phones.Add(new ContactPhone() { Number = "0963600700" });
    contactInfo.Emails.Add(new ContactEmail() { Address = "testAc@msftUwp.com" });
    var contactList = await store.GetContactListAsync(contactListId);
	await contactList.SaveContactAsync(contactInfo);
}
加入之後重新抓取聯絡人列表就可以看到新增進去的EW的聯絡人了。
以及克制詳細的聯絡人資訊。
甚至在People的APP中可以看到建立該Contact的APP[ App1 ]
ContactTracker and ContactTrackerTrigger
接下來介紹的是ContactTracker的部分!
需要啟用ContactTracker在建立Contact的時候就將Tracker的功能開啟並註冊該事件就可以。
public async Task CreateContactStoreAsync()
{
    var success = await TryOpenContactStoreAsync();
	if (!success)
		return;
    var contactLists = await store.FindContactListsAsync();
    if (contactLists.Count != 0)
    {
        System.Diagnostics.Debug.WriteLine("Contact List exit");
        contactListId = contactLists.FirstOrDefault().Id;
    	return;
	}
    var simpleContactList = await store.CreateContactListAsync(contactDisplayName);
    contactListId = simpleContactList.Id;
    simpleContactList.IsHidden = false;
    simpleContactList.OtherAppReadAccess = ContactListOtherAppReadAccess.Full;
	simpleContactList.OtherAppWriteAccess = ContactListOtherAppWriteAccess.SystemOnly;
    store.ChangeTracker.Enable();
	store.ContactChanged += new Windows.Foundation.TypedEventHandler<ContactStore, ContactChangedEventArgs>(ContactStoreTrackerHandler);
	await simpleContactList.SaveAsync();
}
ContactChanged的事件註冊如下
private async void ContactStoreTrackerHandler(ContactStore sender, ContactChangedEventArgs args)
        {
            var defferal = args.GetDeferral();
            var reader = sender.ChangeTracker.GetChangeReader();
            var changes = await reader.ReadBatchAsync();
            while (changes.Count != 0)
            {
                foreach (var change in changes)
                {
                    switch (change.ChangeType)
                    {
                        case ContactChangeType.Created:
                        case ContactChangeType.Modified:
                        case ContactChangeType.Deleted:
                            var toastMessage = string.Format($"{change.Contact.FirstName}, {change.ChangeType}");
                            SentToast(toastMessage);
                            break;
                        case ContactChangeType.ChangeTrackingLost:
                            sender.ChangeTracker.Reset();
                            defferal.Complete();
                            break;
                        default:
                            break;
                    }
                }
                changes = await reader.ReadBatchAsync();
            }
            reader.AcceptChanges();
            defferal.Complete();
        }
這邊的Change type需要注意的是要有ChangeTrackingLost以及Default的預留!當系統出現問題或是進行聯絡人重新設定就會將ChangeTraker重新Refresh所以會Lost調ChangeTrack。至於留下Default則是之後有可能新增其他類型的Type作為預留空間。
當Foreground的event註冊好就可以新增資料來測試!如下就是Create[ Insert contact ]就會提示的Toast Notification。
接下來說明一下Background的TrackEvent是如何被觸發。
之前在前幾篇就有說明到Background的部分所以我就沿用之前的Code
await RegistrationBackgroundTaskHelper.RegisterBackgroundTasksAsync("SimpleBackgroundTask", typeof(ContactStoreTask), new ContactStoreNotificationTrigger());
註冊ContactStoreNotificationTrigger就可以抓取到ContactStore的Tracker。
寫個BackgroundTask的Class並使用以下的Code就可以收到Background的Contact Change的事件
public sealed class BackgroundToastTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            taskInstance.Canceled += TaskInstance_Canceled;
            if (taskInstance.TriggerDetails is ContactStoreNotificationTriggerDetails)
                ShowToast("Contact Changed!!");
        }
        private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
        }
        private void ShowToast(string toastMessage)
        {
            var toastTemplate = ToastTemplateType.ToastText01;
            var toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
            var toastTextElements = toastXml.GetElementsByTagName("text");
            toastTextElements[0].AppendChild(toastXml.CreateTextNode(toastMessage));
            var toast = new ToastNotification(toastXml);
            ToastNotificationManager.CreateToastNotifier("App").Show(toast);
        }
    }
這邊比較可惜的是ContactStoreNotificationTriggerDetails目前只是空的Detail Object...所以沒辦法抓取ChangeType等資訊。
***以上Code以及說明都有可能隨著Windows 10 的版本以及Visual Studio 2015版本有所調整!***
參考資料 2-684 User Data: working with contacts, appointments, text message and more on Windows 10, MSDN Blogs
下次再分享Windows 10 的新技術拉~