Хабрахабр

Получение данных пользователя (добровольное)

Привет, Хабр! Мы продолжаем нашу экспериментальную серию статей, наблюдая за которой вы можете в реальном времени влиять на ход создания игры на UWP. Сегодня на повестке дня — получение данных пользователя. Ведь почти во всех приложениях это — нужная процедура. Присоединяйтесь!

Передаю слово автору, Алексею Плотникову.

Сложно представить игру без социальных функций таких как рейтинговые таблицы, достижения и кланы. Идентификации пользователя является краеугольным камнем большинства современных приложений и, тем более, игр. По большей части, именно проблемы синхронизации и стали отправной точкой для моих изысканий в этом вопросе. В приложениях же лишенных социальных функций, идентификация нужна, чтобы, например, усовершенствовать процесс синхронизации между устройствами.

К сожалению, это хранилище имеет ряд серьезных ограничений. Каждое UWP приложение имеет выделенное облачное хранилище (roaming data), в которое оно может сохранить данные доступные с любого устройства при условии, что пользователь использует тот же MS аккаунт.

Для большинства задач, выделенного размера достаточно, но это все равно ограничение. Во-первых, данные, которые можно поместить в облако, ограничены по размеру. Внутренний алгоритм работы данной функции учитывает множество факторов таких как, например, энергосбережение или лимитированное подключение к интернету и в конечном счете процесс синхронизации между устройствами не только может занять больше десяти минут, но и вовсе не гарантирован (подробнее здесь). Куда более важным ограничением является принцип помещения данных в облако.

Такие ограничения совсем не подходят для моих задач, поэтому процесс синхронизации было решено реализовать самостоятельно и первым делом передо мной стал вопрос в определении устройств, принадлежащих одному и тому же игроку.

Другие имеют собственные сервисы, в которых игроку нужно регистрироваться, но это тоже лишнее усложнение. Многие игры и приложения используют для синхронизации аккаунт пользователя на Facebook, хотя в большинстве случаев это вызвано переносом имеющихся алгоритмов в Microsoft Store, а не удобством реализации. Осталось дело за малым – получить данные пользователя, чтобы идентифицировать его на разных устройствах. В итоге самое логичное решение, конечно же, использовать учетную запись Microsoft, которая однозначно есть у пользователя, так как без нее невозможно приобретение и установка приложений из магазина Windows.

Дело в том, что для получения любых данных о пользователе, он должен осуществить вход в свой аккаунт, а мы должны получить токен доступа, который и позволит нам их запрашивать. Углубляясь в вопрос, я узнал, что данные учетной записи Microsoft получаются с помощью API Microsoft Live, что связано с некоторыми трудностями. Такая процедура создана для безопасности и являются частью стандарта аутентификации OAuth, однако его самостоятельная реализация для начинающих разработчиков кажется сложной и неудобной.

Называется этот инструмент «Диспетчер учетных веб-записей» и именно его мы рассмотрим далее. К счастью, создатели UWP понимали потенциальный ужас, который OAuth способен вызвать у начинающего разработчика и создали изящный и довольно простой способ работы с учетной записью Microsoft (и не только).

Создаем пустой проект и заменяем в файле MainPage.xaml головной Grid, на следующий XAML:

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <Button x:Name="LoginButton" Content="Вход" Click="LoginButton_Click" /> <TextBlock x:Name="UserIdTextBlock"/> <TextBlock x:Name="UserNameTextBlock"/> <Image x:Name="UserAvatarImage" Height="50"/>
</StackPanel>

XAML довольно простой и объяснений не требует, поэтому переходим в событие нажатия кнопки и вставляем следующий код:

AccountsSettingsPane.Show()

Не забудьте добавить импорт пространства имен Windows.UI.ApplicationSettings, иначе данный код не сработает.

Теперь приложение можно выполнить и посмотреть на результат.

Наполнять его мы будем так называемыми «командами», которыми в нашем случае будут перечнем доступных для входа аккаунтов. Пустое окно не совсем то, что мы ожидали, но это всего лишь оболочка, которую сначала нужно наполнить.

Во втором случае используется функция FindAccountProviderAsync, где поставщиком для учетных записей выступит адрес «login.microsoft.com». Инструмент «Диспетчер учетных веб-записей» очень гибкий и позволяет создать эти команды как вручную, так и загрузить их от требуемого поставщика.

Добавим эту строчку перед кодом отображения панели и сразу же сформируем процедуру, вызываемую событием: Однако наполнить панель командами невозможно до ее инициализации, поэтому первым делом нужно подписаться на соответствующее событие.

AddHandler AccountsSettingsPane.GetForCurrentView().AccountCommandsRequested, AddressOf BuildPaneAsync
… Private Sub BuildPaneAsync(sender As AccountsSettingsPane, e As AccountsSettingsPaneCommandsRequestedEventArgs) 'Тут будет код добавления команд
End Sub

К слову, в официальном руководстве подписка на событие AccountCommandsRequested происходит в момент перехода на текущую страницу, а отписка при переходе с нее. Это имеет смысл, если под процесс аутентификации вы выделяете отдельную страницу, но я считаю такой подход недостаточно гибким.

Заполним BuildPaneAsync:

Dim Deferral = e.GetDeferral
Dim msaProvider = Await WebAuthenticationCoreManager.FindAccountProviderAsync("https://login.microsoft.com", "consumers")
Dim command = New WebAccountProviderCommand(msaProvider, AddressOf GetMsaTokenAsync)
e.WebAccountProviderCommands.Add(command)
Deferral.Complete()

И сразу же выполним несколько дополнительных действий:

  • Добавить ключевое слово Async к процедуре;
  • Добавить импорт пространства имен Windows.Security.Authentication.Web.Core;
  • Сформировать процедуру GetMsaTokenAsync с параметром command типа WebAccountProviderCommand

Разберемся с этим кодом. Во-первых, так как для получения списка команд может потребоваться время, нам необходимо отсрочить отображение панели, пока она не будет наполнена. Для этого создается отсроченный объект Deferral. Далее с помощью ранее упомянутой функции FindAccountProviderAsync происходит загрузка поставщика. Обратите внимание, что в эту функцию так же передается параметр со значением «consumers». Это сообщает поставщику, что мы хотим получить стандартную учетную запись Microsoft, а не учетную запись организации (тогда было бы «organizations»).

Последней строчкой мы сообщаем, что панель сформирована и ее можно отображать. После мы создаем новую команду с помощью полученного провайдера, указываем процедуру, вызываемую при нажатии на команду, и добавляем результат на панель.

Вас может ввести в замешательство тот факт, что мы добавляли одну команду на панель, но на ней находится две. Теперь, после запуска и нажатия на кнопку «Вход», отображается заполненная панель с несколькими вариантами входа. С точки зрения внутренней логики оба варианта идентичны и в итоге отсылают нас в процедуру GetMsaTokenAsync, которую мы создали ранее. На самом деле это еще одна из причин по которой мне нравится «Диспетчер учетных веб-записей», так как он самостоятельно определил текущий аккаунт пользователя и предложил его в качестве основного варианта входа, а на случай, если пользователь хочет использовать другую учетную запись, создан отдельный вариант.

Для начала внутри GetMsaTokenAsync нужно выполнить запрос к поставщику данных с указанием типа данных, которые мы запрашиваем, а затем сформировать результат этого запроса: Дальнейшие действия можно разделить на несколько этапов.

Dim request As WebTokenRequest = New WebTokenRequest(command.WebAccountProvider, "wl.basic")
Dim result As WebTokenRequestResult = Await WebAuthenticationCoreManager.RequestTokenAsync(request)

Обратите внимание на параметр «wl.basic», что передается в первую функцию. Это указание на разрешения, которые получает приложение при работе с данными пользователя. Указывая разные разрешения, мы можем работать с адресной книгой, календарем, фотографиями и многими другими данными, но нам нужны только имя, аватар и ID пользователя, поэтому используется wl.basic, которого в данном случае достаточно. Полный список разрешений можно посмотреть здесь.

После успешного получения результата запроса, мы можем сформировать объект типа WebAccount, который поможет получить маркеры доступа к учетной записи:

If result.ResponseStatus = WebTokenRequestStatus.Success Then Dim account As WebAccount = result.ResponseData(0).WebAccount ApplicationData.Current.LocalSettings.Values("CurrentUserProviderId") = account.WebAccountProvider.Id ApplicationData.Current.LocalSettings.Values("CurrentUserId") = account.Id BackgroundConnectUser()
End If

Для работы этого кода понадобится импорт пространств имен Windows.Security.Credentials и Windows.Storage.

Вместо этого мы сохраняем эти маркеры в локальные настройки приложения, чтобы использовать их в будущем для фоновой аутентификации. Полученные маркеры актуальны для текущего пользователя на протяжении длительного времени, а значит у нас нет необходимости повторять процедуру входа постоянно. Собственно, к фоновой аутентификации мы и переходим, вызвав процедуру BackgroundConnectUser.

Сначала добавим импорты, что понадобятся нам для работы дальнейшего кода:

Imports System.Net.Http
Imports Windows.Data.Json

Теперь переходим в BackgroundConnectUser и загружаем сохраненные маркеры:

Dim providerId As String = ApplicationData.Current.LocalSettings.Values("CurrentUserProviderId")
Dim accountId As String = ApplicationData.Current.LocalSettings.Values("CurrentUserId")

Если маркеры не пусты, то формируем из них объекты провайдера и аккаунта для получения токена, а также осуществляем запрос:

Dim provider As WebAccountProvider = Await WebAuthenticationCoreManager.FindAccountProviderAsync(providerId)
Dim account As WebAccount = Await WebAuthenticationCoreManager.FindAccountAsync(provider, accountId)
Dim request As WebTokenRequest = New WebTokenRequest(provider, "wl.basic")
Dim result As WebTokenRequestResult = Await WebAuthenticationCoreManager.GetTokenSilentlyAsync(request, account)

В случае удачи, можно получить токен доступа к API и выполнять непосредственные запросы:

If result.ResponseStatus = WebTokenRequestStatus.Success Then Dim token As String = result.ResponseData(0).Token Dim restApi = New Uri("https://apis.live.net/v5.0/me?access_token=" + token) Dim client = New HttpClient() Dim infoResult = Await client.GetAsync(restApi) Dim Content As String = Await infoResult.Content.ReadAsStringAsync() Dim jsonO = JsonObject.Parse(Content) UserIdTextBlock.Text = jsonO("id").GetString UserNameTextBlock.Text = jsonO("name").GetString UserAvatarImage.Source = New BitmapImage(New Uri("https://apis.live.net/v5.0/me/picture?access_token=" + token))
End If

Адреса запросов для получения данных пользователя можно узнать в документации по API Microsoft Live, но нас интересуют всего два: «https://apis.live.net/v5.0/me» для получения общих данных и «https://apis.live.net/v5.0/me/picture» для получения аватара. Возвращаются данные пользователя в формате Json, поэтому для их удобного разбора используется парсер.

Связано это с тем, что API Microsoft Live не предоставляют данные просто так. Наконец можно запустить программу и убедится, что все равно ничего не работает. К счастью, идентификатор формируется автоматически при создании нового приложения в информационной панели разработчика. Для их использования приложение должно иметь идентификатор в службе Microsoft Live к которому затем привязываются разрешения на доступ к данным. Для этого перейдите в меню «Проект > Магазин > Связать приложение с Магазином…» и выберете нужное имя из списка (или зарегистрируйте новое). Чтобы загрузить этот идентификатор в текущий проект, достаточно просто связать его с магазином Windows. Больше нам делать ничего не нужно, так как необходимые данные в API Microsoft Live будут переданы автоматически.

Повторно запускаем проект и наконец наблюдаем результат.

На этом рассмотрение получения данных пользователя можно было бы завершить, если бы я ставил перед собой цель просто пересказать материал из официального руководства.

Ниже я приведу полный код проекта с внесенными в него изменениями, а далее объясню и обосную каждое такое изменение.

XAML:

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel.Resources> <local:ConnectStatusToEnabledConverter x:Key="ConnectStatusToEnabledConverter"/> </StackPanel.Resources> <StackPanel Orientation="Horizontal"> <Button x:Name="LoginButton" Content="Вход" Click="LoginButton_Click" IsEnabled=", ConverterParameter=0}"/> <Button x:Name="LogOutButton" Margin="10,0,0,0" Content="Выход" Click="LogOutButton_Click" IsEnabled="{Binding ConnectStatus, Converter={StaticResource ConnectStatusToEnabledConverter}, ConverterParameter=2}"/> </StackPanel> <TextBlock Margin="0,0,0,20" Text="{Binding ConnectStatus}"/> <TextBlock>ИД пользователя: <Run FontWeight="Bold" Text="{Binding UserId}"/></TextBlock> <TextBlock>Имя пользователя: <Run FontWeight="Bold" Text="{Binding UserName}"/></TextBlock> <Image Height="50" Source="{Binding UserAvatar}" HorizontalAlignment="Left"/>
</StackPanel>

Код Окна

Imports Windows.Security.Authentication.Web.Core
Imports Windows.UI.ApplicationSettings
Imports Windows.Security.Credentials
Imports Windows.Storage
Imports System.Net.Http
Imports Windows.Data.Json
Imports Windows.System Public NotInheritable Class MainPage Inherits Page Private CurUser As New UserManager Private Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded DataContext = CurUser End Sub Private Async Sub LoginButton_Click(sender As Object, e As RoutedEventArgs) If Not Await CurUser.BackgroundConnectUser Then CurUser.ConnectUser() End If End Sub Private Async Sub LogOutButton_Click(sender As Object, e As RoutedEventArgs) CurUser.LogOutUser(Await CurUser.GetWebAccount) End Sub
End Class
Public Class UserManager Implements INotifyPropertyChanged
#Region "Реализация интерфейса" Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(PropertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName)) End Sub
#End Region Private UserIdValue As String Private UserNameValue As String Private UserAvatarValue As BitmapImage Private ConnectStatusValue As UserConnectStatusEnum = UserConnectStatusEnum.None #Region "Свойства" ''' <summary> ''' ID текущего пользователя ''' </summary> ''' <returns></returns> Public Property UserId As String Get Return UserIdValue End Get Set(value As String) UserIdValue = value OnPropertyChanged("UserId") End Set End Property ''' <summary> ''' Имя текущего пользователя ''' </summary> ''' <returns></returns> Public Property UserName As String Get Return UserNameValue End Get Set(value As String) UserNameValue = value OnPropertyChanged("UserName") End Set End Property ''' <summary> ''' Аватар пользователя ''' </summary> ''' <returns></returns> Public Property UserAvatar As BitmapImage Get Return UserAvatarValue End Get Set(value As BitmapImage) UserAvatarValue = value OnPropertyChanged("UserAvatar") End Set End Property ''' <summary> ''' Статус подключения ''' </summary> ''' <returns></returns> Public Property ConnectStatus As UserConnectStatusEnum Get Return ConnectStatusValue End Get Set(value As UserConnectStatusEnum) ConnectStatusValue = value OnPropertyChanged("ConnectStatus") End Set End Property
#End Region
#Region "Основыне функции и процедуры" ''' <summary> ''' Запускает процедуру входа пользователя ''' </summary> Public Sub ConnectUser() AddHandler AccountsSettingsPane.GetForCurrentView().AccountCommandsRequested, AddressOf BuildPaneAsync AccountsSettingsPane.Show() End Sub Public Async Sub LogOutUser(account As WebAccount) ApplicationData.Current.LocalSettings.Values.Remove("CurrentUserProviderId") ApplicationData.Current.LocalSettings.Values.Remove("CurrentUserId") Await account.SignOutAsync() UserId = "" UserName = "" UserAvatar = New BitmapImage ConnectStatus = UserConnectStatusEnum.None End Sub Public Async Function BackgroundConnectUser() As Task(Of Boolean) Dim result As Boolean = False Dim account As WebAccount = Await GetWebAccount() If account Is Nothing Then Return result ConnectStatus = UserConnectStatusEnum.Logon Dim request As WebTokenRequest = New WebTokenRequest(account.WebAccountProvider, "wl.basic") Dim requestResult As WebTokenRequestResult = Await WebAuthenticationCoreManager.GetTokenSilentlyAsync(request, account) If requestResult.ResponseStatus = WebTokenRequestStatus.Success Then Try Dim token As String = requestResult.ResponseData(0).Token Dim restApi = New Uri("https://apis.live.net/v5.0/me?access_token=" + token) Dim client = New HttpClient() Dim infoResult = Await client.GetAsync(restApi) Dim Content As String = Await infoResult.Content.ReadAsStringAsync() Dim jsonO = JsonObject.Parse(Content) UserId = jsonO("id").GetString UserName = jsonO("name").GetString UserAvatar = New BitmapImage(New Uri("https://apis.live.net/v5.0/me/picture?access_token=" + token)) ConnectStatus = UserConnectStatusEnum.Ssuccessful result = True Catch ex As Exception ConnectStatus = UserConnectStatusEnum.None End Try Else ConnectStatus = UserConnectStatusEnum.None End If Return result End Function #End Region
#Region "Внутренние функции, процедуры и типы" ''' <summary> ''' Создает варианты логина на панели ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Async Sub BuildPaneAsync(sender As AccountsSettingsPane, e As AccountsSettingsPaneCommandsRequestedEventArgs) RemoveHandler AccountsSettingsPane.GetForCurrentView().AccountCommandsRequested, AddressOf BuildPaneAsync Dim Deferral = e.GetDeferral Dim msaProvider = Await WebAuthenticationCoreManager.FindAccountProviderAsync("https://login.microsoft.com", "consumers") Dim command = New WebAccountProviderCommand(msaProvider, AddressOf GetMsaTokenAsync) e.WebAccountProviderCommands.Add(command) e.HeaderText = "Для доступа к ферме требуется выполнить вход в ваш аккаунт Microsoft" Dim settingsCmd As SettingsCommand = New SettingsCommand("settings_privacy", "Политика конфиденциальности", Async Sub() Await Launcher.LaunchUriAsync(New Uri("https://privacy.microsoft.com/ru-ru/")) End Sub) e.Commands.Add(settingsCmd) Deferral.Complete() End Sub ''' <summary> ''' Логин и получение токина ''' </summary> ''' <param name="command"></param> Private Async Sub GetMsaTokenAsync(command As WebAccountProviderCommand) ConnectStatus = UserConnectStatusEnum.Logon Dim request As WebTokenRequest = New WebTokenRequest(command.WebAccountProvider, "wl.basic") Dim result As WebTokenRequestResult = Await WebAuthenticationCoreManager.RequestTokenAsync(request) If result.ResponseStatus = WebTokenRequestStatus.Success Then Dim account As WebAccount = result.ResponseData(0).WebAccount ApplicationData.Current.LocalSettings.Values("CurrentUserProviderId") = account.WebAccountProvider.Id ApplicationData.Current.LocalSettings.Values("CurrentUserId") = account.Id Await BackgroundConnectUser() Else ConnectStatus = UserConnectStatusEnum.None End If End Sub ''' <summary> ''' Получает аккаунт пользователя на основе сохраненных маркеров ''' </summary> ''' <returns></returns> Public Async Function GetWebAccount() As Task(Of WebAccount) Dim providerId As String = ApplicationData.Current.LocalSettings.Values("CurrentUserProviderId") Dim accountId As String = ApplicationData.Current.LocalSettings.Values("CurrentUserId") If (providerId Is Nothing And accountId Is Nothing) Then Return Nothing End If Dim provider As WebAccountProvider = Await WebAuthenticationCoreManager.FindAccountProviderAsync(providerId) Dim account As WebAccount = Await WebAuthenticationCoreManager.FindAccountAsync(provider, accountId) Return account End Function ''' <summary> ''' Перечислитесь статуса подключения ''' </summary> Public Enum UserConnectStatusEnum ''' <summary> ''' Вход не выполнен ''' </summary> None = 0 ''' <summary> ''' Осуществляется вход ''' </summary> Logon = 1 ''' <summary> ''' Вход выполнен ''' </summary> Ssuccessful = 2 End Enum
#End Region
End Class
Public Class ConnectStatusToEnabledConverter Implements IValueConverter Public Function Convert(value As Object, targetType As Type, parameter As Object, language As String) As Object Implements IValueConverter.Convert Return CStr(parameter).IndexOf(CStr(value)) > -1 End Function Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, language As String) As Object Implements IValueConverter.ConvertBack Throw New NotImplementedException() End Function
End Class

Разметка страницы претерпела не очень большие корректировки. Самое важное отличие — это добавление кнопки выхода из аккаунта, а также строки для отслеживания статуса подключения. Остальные изменение сделали отображение данных более наглядным ведь в конечном итоге задача страницы продемонстрировать результат выполнения кода, в котором и состоялись основные изменения. Рассмотрим их.

Это сделано для того, чтобы иметь доступ к этому коду из любого места приложения. Во-первых, я полностью вынес логику работы с диспетчером учетных веб-записей в отдельный класс. Например, в статье «Расширенный экран-заставка» я упоминал о длительных операциях, которые имеет смыл выполнять внутри экрана-заставки и процесс получения данных пользователя в BackgroundConnectUser как раз является такой операцией.

Вся работа внутри страницы сводится к реакции на нажатие двух кнопок и установке контекста данных для страницы (в процедуре MainPage_Loaded). В результате переноса кода в отдельный класс, в классе самой страницы практически ничего не осталось. Класс реализует интерфейс INotifyPropertyChanged, что позволяет осуществлять привязку к его полям из разметки страницы. Не сложно догадаться, что в качестве контекста данных выступает экземпляр класса UserManager в котором уже и происходит основная работа.

Перенесенный код получил некоторые изменения, и я постараюсь разобрать их в том же порядке, в котором создавался первоначальный пример:

  • Процедура ConnectUser. Она по сути дублирует код, что ранее вызывался по нажатию на кнопку «Вход». Вызов это процедуры понадобится только в том случае, если у нас нет маркеров для фонового подключения.
  • BuildPaneAsync получила несколько значимых, и не очень изменений. Для начала отписка от события AccountCommandsRequested, которое после вызова данной процедуры уже нам не нужно. Далее следует уже известный код, который дополняется установкой подзаголовка в окне диспетчера и добавлением ссылки на политику конфиденциальности. Оба этих пункта не обязательны и реализуются полностью по вашему усмотрению. Мало того, во втором случае можно добавить ссылку, выполняющую любой ваш код и не обязательно это переход на страницу с политикой конфиденциальности.

  • После нажатия на кнопку «Продолжить» начинается выполнение процедуры GetMsaTokenAsync, поэтому первой же строкой в ней устанавливается статус Logon, на который можно реагировать для отображения индикаторов выполнения или блокировки элементов интерфейса. Затем идет уже знакомый нам код, но с добавлением реакции на ситуации, когда вход был отменен или не прошел успешно. Признаюсь, над этим, казалось бы, банальным местом я бился целый день. Загвоздка в том, что при первом выполнении данного кода, сразу после окна с выбором вариантов входа, появляется еще одно окно с запросом разрешения на доступ к данным. Между переключениями этих двух окон может пройти относительно большой промежуток времени (до 2х секунд), поэтому логично сохранить статус Logon в этот период, чтобы исключить повторное нажатие на кнопку «Вход». Однако пользователь может отказаться предоставлять доступ к данным, либо просто закрыть второе окно (нажать кнопку «Назад» на мобильном устройстве), поэтому в случае таких действий нужно вернуть исходный статус None, что мы и делаем в блоке Else.
  • Процедура BackgroundConnectUser в новой вариации превратилась в асинхронную функцию, чтобы мы могли соответствующим образом отреагировать на неудачную (или удачную) попытку фонового получения данных. Так как данная функция может вызываться из разных мест (например, из экрана-заставки), то мы должны убедится, что загруженные маркеры доступа не являются пустыми. Для удобства загрузки сохраненных маркеров, создана отдельная функция GetWebAccount и, если в локальных настройках маркеров нет, то она возвращает Nothing. При неудаче получения объекта WebAccount, возвращаем результат по умолчанию (False), а если он получен удачно, то устанавливаем статус в Logon и продолжаем выполнение процедуры. Дале повторяем уже известные действия с той лишь разницей, что в случае отказа в получении токена, нужно вернуть статус None, а при удачном запросе Ssuccessful.
  • Процедура LogOutUser содержит код, не используемый ранее. В ней реализуется возможность выхода из аккаунта и удаления маркеров из сохраненных настроек. Обязательно реализуйте такую возможность в вашем приложении, так как это даст полную свободу пользователю с точки зрения обеспечения конфиденциальности, не говоря уже о потенциальной необходимости войти с другими учетным данными.
  • Ну последнее, что следует упомянуть это свойство ConnectStatus, которое я создал в данном классе. Это свойство имеет тип собственного перечислителя UserConnectStatusEnum и необходима для установки статуса входа. Это важный элемент взаимодействия с классом, так как благодаря привязке к этому свойству мы можем заблокировать кнопки, нажатие на которые не желательно в момент выполнения определенных участков кода или, напротив, разблокировать те, что доступны только после выполнения входа. Чтобы привязка свойства IsEnabled к ConnectStatus интерпретировалась правильно, так же создан конвертер ConnectStatusToEnabledConverter.

На этом моя реализация для диспетчера учетных веб-записей заканчивается. Само-собой в процессе разработки будут внесены какие-то доработки, которые будут отвечать тем задачам, что лично я ставлю перед данным инструментом, однако приведенный класс вполне работоспособен и вы можете смело копировать его в свой проект, чтобы приступать к реализации ваших задач, связанных с получением данных пользователя.

Именно об этом мы и поговорим в следующей статье. Теперь, когда данные пользователя получены, можно приступать к изучению вопроса синхронизации данных между устройствами и начну я с поиска походящей площадки и технологии.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть