Azure & Windows Phone and Windows Store Apps Technical Guru – July 2004

The The Microsoft TechNet Guru Awards! (July 2014) from TechNet Wiki is out!

I won

  • Windows Phone and Windows Store Apps Technical Guru - July 2014  (Gold)
  • Microsoft Azure Technical Guru - July 2014  (Bronze)

Here are the comments

guru windows
guru azure

I am really happy! :)

You can read my article here:

 

Creating framework documentation with GhostDoc

GhostDoc is a Visual Studio extension that automatically generates XML documentation comments for methods and properties based on their type, parameters, name, and other contextual information.

ghostdoc in visual studio

GhostDoc has two versions: Free and Pro, for see the difference see this reference.

One advantage about the GhostDoc Free is, if you follow good naming conventions in your code, then you will get very decent results on the summary GhostDoc generates.  When we see code that is not documented, it is as simple as hit Ctrl-Shift-D to have GhostDoc document it.

Let’s see an example!

We will use an open source project, the Xamarin-Forms-Labs

The PhoneService has a method without comments:
without comments
Using GhostDoc Free, we can use Ctrl-Shift-D to comment that method, here is the result:
with comment

With this tools there isn´t reason for don´t have comments in your code.

GhostDoc Pro, has more feature about it, like

  • XML Comment Preview
  • Document This
  • Document File
  • Document Type

and others….

After the code is commented is great to have a helper for provide with the framework, and GhostDoc Pro has the Build Helper File feature for generate helper files based in the comments.

Here is the “Build Help File” for Xamarin-Forms-Labs

build helper

Like we can see in the image, we can

  • Define the output folder
  • Define the type of the helper file to be generate
  • Scope
  • Define the header and the footer
  • Select the project we want to include in the project

The chm file is good for offline usage and the website is useful for publish the framework online. In this case, in Xamarin-Forms-Labs project, there isn´t any website and only exist the Github repository and for help to expose the helper online, was used the

GitHub & BitBucket HTML Preview

For see the results, see the following reference:

 

If you want to see GhostDoc in practice, see this overview

Win the C# Corner Most Valuable Professional Award

In the last months I have been participate in C# Corner, here I published some articles.

This week I was suprissed when I saw the C# Corner Q2, 2014 MVPs Announced and my name is in the list of the new C# Corner MVPs! Really Nice :)

C# Corner MVP Logo

Analyzing C# code using NDepend

Introduction

Best practices in Software Development is the right way to have a good quality in Software and nowadays the developers/architects/managers has more consciousness about it or should have!

There are a lot of different tools for measuring the quality of software development, NDepend is one of these tools.

This article has the goal to show the analyse  made by NDepend and the main focus are query and rules, metrics and dependency graph, but NDepend has more features.

 

Description

NDepend is a Visual Studio static analyzer: analyse layering, specify design rules, plan refactoring, compare different versions of the code, software metrics, dependency graphs, dependency matrix, declarative code query, and more!

ndepend visual studio

There is a trial version for 14 days, we can get it here and there is a Visual Studio Extension that can be find here.
This tool will analyse the dll output from the project and at the end it allow to show different metrics, graphics, diagrams and rules. Here are what the analyse of NDepend can tell us:

  • Application Metric: This section gives you an idea of the topology of your application.
  • Assembly Metric: This section gives you an idea of the size of each assembly within your application in terms of #IL instructions and others metrics.
  • VisualNDepend View: This section illustrates the size of your assemblies, namespaces, types and methods in terms of #IL instructions. You can browse this view dynamically by clicking the Launch Visual NDepend on this Project icon in the NDepend.Project UI.
  • Assembly Abstracness vs. Instability: This section illustrate the Abstracness/Instability principle explained in the assemblies metrics section.
  • Assembly Dependencies: This section shows all dependencies between assemblies of your application in a table.
  • Assembly Dependency Diagram: This section shows all dependencies between assemblies of your application in a diagram
  • Assembly build order: This section gives you one of the possible build order for your assemblies. If a cycle exists in your assemblies dependencies graph, this section will report it.
  • NDepend information and warnings: This sections gives you advices about your code:
    • It warns you when an assembly depends on a less stable assembly than itself.
    • It warns you when the visibility of a type or of a member is not optimal (in the context of the analysed application).
    • It warns you when a type or a member is not used (in the context of the analysed application).
  • CQLinq Queries and Rules: This section reports default CQLinq rules which have been violated. More about CQLinq here.
  • Type Metrics: This section recaps type metrics in a table. A link to the documentation is provided for each metric.

 

This article will use an Azure Mobile Service as a demo project, but could be another project type.

After opened the project, we should attach the NDepend project to the Azure Mobile Service project
ndepend attach project

After attach the project, we can choice the output folder and which dll we want to analyse

ndepend choice output

For help to understood each dll, here is an overview:

  • CustomersSample.Service is the dll output for the project that contains the Azure Mobile Service that will be deploy to the server.
  • CustomersSample.Pcl is the dll that contains the entities that can be used in client apps (like Windows Phone, Windows Store Apps, Xamarin or in Tests.)
  • CustomersSample.Tests is the dll that contains tests for help to understand if the service is working correctly. This project simulate a client app doing requests to the service.

Note: For help in development, the Pcl contains the dtos file from Service, but these files are “added as linked”, and was used directives (#if !PCL #else #endif) for change the names and for remove the base class from dtos (each dto is a EntityData).

During the analyse we can see the analysis error list:

during the analyze

Is a little strange to have this message

WARNING: No application or third party assembly found in directory {C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\WPF}

Because we are analyzing an Azure Mobile Service and there is nothing related with WPF. It make me sense for WPF project not for this project.

 

After the analyse, is showed a windows that allow to choose what we want to see

menu

But after this choose we can see the other options, using the Visual Studio menu

visual studio menu

All output can be seen in Visual Studio, but after the analyse, an html page will be open in our default browser that contains an overview about the output.

Yes, there are two ways to see the results, in browser and in Visual Studio. This is a good thing for managers, for example.

See the output in project folder, for this project look like this

output folder

In the browser it looks like it

ndepend output in browser

See the output for this demo here.

In Visual Studio, is recommended to open the dashboard, because it contains an interesting overview.
queries and rules

This overview allow to make some conclusion about the code, for example this sample has 81,77% of comments that is not bad. The graphics don´t have so much information because was the first time we ran the NDepend for this project.

For understood the metrics in dashboard, see this reference.

Note: Code Coverage by Tests is empty because is needed to import this information.  NDepend can import coverage data from:

 

Code Rules section has some violations, rules that are ok and inactive. And there is one rule that should be seen, because there is a Critical Rule Violated! Really?

Let’s see it!

We can click in “Critical Rule Violated” and a window will be opened

critical rule violated

The rule violated is “Avoid having different types with same name” and we can click in the “Avoid having different types with same name” for see more details. And in the right windows we will see that. There are two classes that has repeated name, see it in solution explorer

solution explorer

Is true, is used the same name in different dlls, but these dlls will never use together and the NDepend should be smart enough for understand these types are not used in the same context.

Is possible to see the query used to verify this rule
query

The question in this moment is can I change this query to support this specific case?

Yes, we can change the query used and should create the query for support this.

Later we will see a simple case where we will change the query.

 

Let’s see other violation, for it click in left menu:

queries and rules

Is possible to filter for see, for example, the Rules Violated, here is the list

filter rule violated

There are rules that should be fixed, for example:

  • Methods too big
  • UI layer shouldn’t use directly DAL layer
  • UI layer shouldn’t use directly DB types

and others…

But there are rules that not make sense, for example:

Instance fields should be prefixed with a ‘m_’ : some developers use the keyword “this” and other uses “_” as a prefix for the field name.

And the question is, why “m_”? This reference help in that response, and we can change it for that we want!

change query

With it, is very simple to change the rules for that we want!

There are more queries that need to be changed, another example is

  • Class with no descendant should be sealed if possible: this is another great rule, but if the model is related with database and is using EF and the properties are virtual, the class cannot be sealed.

See more about change queries rules here and here.

NDepend generate a Dependency Graph, that is very interesting, Visual Studio Ultimate has this feature too. Here is the result for this project
graph

This graph should be clean, if it show a lot of connection and looks confuse it means your code has a lot of dependencies and maybe something is not ok. If you are using dependency injection your graph will be clean and beautiful.

Note: This project don´t use DI, but it is a small project for this reason not looks confuse and gives the idea that all is ok.

NDepend has more diagrams: Dependency Matrix, Treemap Metric View and Abstractness vs. Instability, but they are complex, because in a first attempt is not easy to understand what they mean.

See the complete list of feature here.

For finalize, is important to define all options related with this tool. For example, define when the NDepend report should be done

options
See more about it in this reference Rules Execution Options.

 

 Conclusion

In conclusion, NDepend is a great tool for measuring the quality of software development for a .Net projects. In this article we only generated twice the reports but for a real project it can be useful for follow the metrics, violated rules… The possibility of editing the queries rules is a plus for this tool.

 

See Also

Request for IOS and Android devs: Packaging and Publishing Experiences

The Visual Studio team is looking for Android and iOS developers who can provide feedback on app packaging and publishing experiences. This survey can take as little as 5 minutes to complete and will provide direct feedback to support changes in future versions of Visual Studio.

 

The information you provide is entirely voluntary. If you do not wish to provide us with any information, please disregard this survey. By filling out and returning this survey, you agree that we can use, disclose, reproduce, or otherwise distribute your feedback at the aggregated level. Your personal information will remain confidential. Please check http://privacy.microsoft.com/en-us/default.aspx for Microsoft Privacy Statements.

 

Use this url: https://illumeweb.smdisp.net/collector/Survey.ashx?Name=PackagePublish

Authentication using Facebook, Google and Microsoft accounts in Universal Apps using MVVM

Introduction

This sample shows how to connect universal apps to Facebook, Google and Microsoft accounts using the MVVM pattern.

Building the Sample

You only need Visual Studio 2012 or Visual Studio 2013 and Windows 8 or Windows 8.1, both the RTM version.

Description

Recently published was Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM) and this sample has the same goal, but now the target is Universal Apps. Both have the goal to use the MVVM pattern.

Before starting this sample, analize to see if the SDKs used in the first sample could be used in this new sample and in a first attempt didn´t find a package for all the targets (Windows 8.1 and Windows Phone 8.1 Runtime). Let’s see that analysis.

The packages used was:

  1. Facebook SDK for Windows Phone (http://facebooksdk.net/docs/phone/ )
  2. Google APIs Auth Client and Google APIs OAuth2 Client (https://www.nuget.org/packages/Google.Apis.Auth/ andhttps://www.nuget.org/packages/Google.Apis.Authentication/1.6.0-beta )
  3. Live SDK (http://msdn.microsoft.com/en-US/onedrive/dn630256 )

The packages that were analized are:

  1. Facebook SDK for Windows Phone: there is a package for Windows 8.1 Store Apps but there isn’t for Windows Phone 8.1 Runtime (we cannot use the version from Windows Phone 8.0 because it uses a namespace for controls that does not exist in the Windows Phone 8.1 Runtime)
  2. Google APIs Auth Client and Google APIs OAuth2 Client: there is a package compatible with Windows 8.1 Store Apps but it is a bit different from the sample created before, the API changed. And there isn’t a package for the Windows Phone 8.1 Runtime.
  3. Live SDK: is compatible with the Windows Phone 8.1 Runtime and Windows 8.1.

The next step, was to try to port the package for Google from Windows 8.1 Store Apps to Windows Phone 8.1 Runtime, create the logic because there is a lot of code shared between them and then the hard work began.

After some attempts, the code started to throw the exception NotImplementedException because theWebAuthenticationBroker class does not work the same way for these targets. There is a sample that shows this difference, here is the source code  and we will see this in this sample.

In conclusion of this analysis, I decided to use WebAuthenticationBroker for authentication using Facebook and Google accounts and Live SDK for Microsoft accounts.

Let’s start the sample!

Note: This sample uses MVVM Light and Cimbalino Toolkit.

For each provider it is necessary to get the app id/client id/client secrect in their websites.

For Google go to https://console.developers.google.com/project and create a new project (APIs and auth > credentials).

Universal Apps using MVVM

For Facebook go to https://developers.facebook.com/ and create a new app.

For Live SDK go to https://account.live.com/developers/applications/index and create one or use an existing app.

Before you start you should change the Constant file to add client ids / client secret / app id, without it the app fails!!

 

/// <summary>   
    /// Defines the constants strings used in the app.   
    /// </summary>   
    public class Constants   
    {   
        /// <summary>   
        /// The google callback url.   
        /// </summary>   
       #if !WINDOWS_PHONE_APP 
       public const string GoogleCallbackUrl = "urn:ietf:wg:oauth:2.0:oob"; 
       #else 
        public const string GoogleCallbackUrl = "http://localhost"; 
       #endif  
   
        /// <summary>   
        /// The facebook app id.   
        /// </summary>   
        public const string FacebookAppId = "<app id>";   
   
        /// <summary>   
        /// The google client identifier.   
        /// </summary>   
        public const string GoogleClientId = "<client id>";   
           
        /// <summary>   
        /// The google client secret.   
        /// </summary>   
        public const string GoogleClientSecret = "<client secret";   
   
        /// <summary>   
        /// The login token.   
        /// </summary>   
        public const string LoginToken = "LoginToken";   
           
        /// <summary>   
        /// The facebook provider.   
        /// </summary>   
        public const string FacebookProvider = "facebook";   
   
        /// <summary>   
        /// The google provider.   
        /// </summary>   
        public const string GoogleProvider = "google";   
   
        /// <summary>   
        /// The microsoft provider.   
        /// </summary>   
        public const string MicrosoftProvider = "microsoft";   
    }

This sample will use the same idea used in Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM). There are classes that have the same goal and the same name. Like in that sample, this sample created a SessionService that manages the Login and Logout using a provider value. That is nice because in LoginView we set the buttons to the same command and for each command we set the provider in commandparameter. With it, the LoginView and LoginViewModel are more clear and simple.

The following are the classes created:

  • FacebookService has all code related with authentication with Facebook account;
  • MicrosoftService has all code related with authentication with Microsoft account;
  • GoogleService has all code related with authentication with Google account;
  • SessionService call the methods login or logout for the provide requested;

The Flow

To help to understood the difference in the flow in each platform some diagrams were created.

The flow for the Windows 8.1 Store apps will be:

The flow for Windows Phone 8.1 Runtime will be:

  • using Microsoft account
  • using Facebook Account or Google account

Note: Start in LoginView using the blue arrow and when the blue flow finnishes, go to FacebookService/GoogleService and follow the red flow.

Like we can see, the authentication for Windows Phone 8.1 is very complicated, it could be easier like in Windows 8.1 Store apps. And It breaks the MVVM Pattern!

This sample is a Universal App, for this reason the code can be found in the Shared Project and to add specific features for each target directives (#if #else #endif) are used, it can cause some difficulties for understanding the code but is a good way to have only one code in one place. Partial methods and classes can be used here, because in most cases directives are added to add a method for Windows Phone.

The FacebookService

/// <summary>   
    /// Defines the Facebook Service.   
    /// </summary>   
    public class FacebookService : IFacebookService   
    {   
        private readonly ILogManager _logManager;   
   
        /// <summary>   
        /// Initializes a new instance of the <see cref="FacebookService"/> class.   
        /// </summary>   
        /// <param name="logManager">   
        /// The log manager.   
        /// </param>   
        public FacebookService(ILogManager logManager)   
        {   
            _logManager = logManager;   
        }   
   
        /// <summary>   
        /// The login sync.   
        /// </summary>   
        /// <returns>   
        /// The <see cref="Task"/> object.   
        /// </returns>   
        public async Task<Session> LoginAsync()   
        {   
            const string FacebookCallbackUrl = "https://m.facebook.com/connect/login_success.html";   
            var facebookUrl = "https://www.facebook.com/dialog/oauth?client_id=" + Uri.EscapeDataString(Constants.FacebookAppId) + "&redirect_uri=" + Uri.EscapeDataString(FacebookCallbackUrl) + "&scope=public_profile,email&display=popup&response_type=token";   
   
            var startUri = new Uri(facebookUrl);   
            var endUri = new Uri(FacebookCallbackUrl);   
  
#if WINDOWS_PHONE_APP   
            WebAuthenticationBroker.AuthenticateAndContinue(startUri, endUri, null, WebAuthenticationOptions.None);   
            return null;   
#else   
            var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, startUri, endUri);   
            return GetSession(webAuthenticationResult);   
#endif   
        }   
           
        private void GetKeyValues(string webAuthResultResponseData, out string accessToken, out string expiresIn)   
        {   
            string responseData = webAuthResultResponseData.Substring(webAuthResultResponseData.IndexOf("access_token", StringComparison.Ordinal));   
            string[] keyValPairs = responseData.Split('&');   
            accessToken = null;   
            expiresIn = null;   
            for (int i = 0; i < keyValPairs.Length; i++)   
            {   
                string[] splits = keyValPairs[i].Split('=');   
                switch (splits[0])   
                {   
                    case "access_token":   
                        accessToken = splits[1];   
                        break;   
                    case "expires_in":   
                        expiresIn = splits[1];   
                        break;   
                }   
            }   
        }   
   
        /// <summary>   
        /// This function extracts access_token from the response returned from web authentication broker   
        /// and uses that token to get user information using facebook graph api.    
        /// </summary>   
        /// <param name="accessToken">   
        /// The access Token.   
        /// </param>   
        /// <returns>   
        /// The <see cref="Task"/>.   
        /// </returns>   
        private async Task<UserInfo> GetFacebookUserNameAsync(string accessToken)   
        {   
            var httpClient = new HttpClient();   
            var response = await httpClient.GetStringAsync(new Uri("https://graph.facebook.com/me?access_token=" + accessToken));   
            var value = JsonValue.Parse(response).GetObject();   
            var facebookUserName = value.GetNamedString("name");   
   
            return new UserInfo   
            {   
                Name = facebookUserName,   
            };   
        }   
   
        /// <summary>   
        /// Logouts this instance.   
        /// </summary>   
        public async void Logout()   
        {   
            Exception exception = null;   
            try   
            {   
                  
            }   
            catch (Exception ex)   
            {   
                exception = ex;   
            }   
            if (exception != null)   
            {   
                await _logManager.LogAsync(exception);   
            }   
        }   
  
#if WINDOWS_PHONE_APP   
        public async Task<Session> Finalize(WebAuthenticationBrokerContinuationEventArgs args)   
        {   
            Exception exception = null;   
            try   
            {   
                var result = args.WebAuthenticationResult;   
   
                return GetSession(result);   
            }   
            catch (Exception e)   
            {   
                exception = e;   
            }   
   
            await _logManager.LogAsync(exception);   
              
            return null;   
        }   
#endif   
        private Session GetSession(WebAuthenticationResult result)   
        {   
            if (result.ResponseStatus == WebAuthenticationStatus.Success)   
            {   
                string accessToken;   
                string expiresIn;   
                GetKeyValues(result.ResponseData, out accessToken, out expiresIn);   
   
                return new Session   
                {   
                    AccessToken = accessToken,   
                    ExpireDate = new DateTime(long.Parse(expiresIn)),   
                    Provider = Constants.FacebookProvider   
                };   
            }   
            if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)   
            {   
                throw new Exception("Error http");   
            }   
            if (result.ResponseStatus == WebAuthenticationStatus.UserCancel)   
            {   
                throw new Exception("User Canceled.");   
            }   
            return null;   
        }   
    }

In this class, in LoginAsync, we could see the directives that define the code for each platform. The first attempt was a bit complicated to define a solution for it, using null was the solution (for Windows Phone, of course!). This class doesn´t do anything in the logout method, we could remove it, but to keep the same pattern in all the classes it wasn´t removed and the session is not saved in webview, at least it is possible to login using a different account when the LoginView is shown again.

The GoogleService

public class GoogleService : IGoogleService
    {
        private readonly ILogManager _logManager;

        /// <summary>
        /// Initializes a new instance of the <see cref="GoogleService"/> class.
        /// </summary>
        /// <param name="logManager">
        /// The log manager.
        /// </param>
        public GoogleService(ILogManager logManager)
        {
            _logManager = logManager;
        }

        /// <summary>
        /// The login async.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/> object.
        /// </returns>
        public async Task<Session> LoginAsync()
        {
            var googleUrl = new StringBuilder();
            googleUrl.Append("https://accounts.google.com/o/oauth2/auth?client_id=");
            googleUrl.Append(Uri.EscapeDataString(Constants.GoogleClientId));
            googleUrl.Append("&scope=openid%20email%20profile");
            googleUrl.Append("&redirect_uri=");
            googleUrl.Append(Uri.EscapeDataString(Constants.GoogleCallbackUrl));
            googleUrl.Append("&state=foobar");
            googleUrl.Append("&response_type=code");
          
            var startUri = new Uri(googleUrl.ToString());
           

#if !WINDOWS_PHONE_APP
           var endUri = new Uri("https://accounts.google.com/o/oauth2/approval?");
           var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, startUri, endUri);
           return await GetSession(webAuthenticationResult);
#else
            WebAuthenticationBroker.AuthenticateAndContinue(startUri, new Uri(Constants.GoogleCallbackUrl), null, WebAuthenticationOptions.None);
            return null;
#endif
        }
        
        private string GetCode(string webAuthResultResponseData)
        {
            // Success code=4/izytpEU6PjuO5KKPNWSB4LK3FU1c
            var split = webAuthResultResponseData.Split('&');

            return split.FirstOrDefault(value => value.Contains("code"));
        }

        /// <summary>
        /// The logout.
        /// </summary>
        public void Logout()
        {
        }

#if WINDOWS_PHONE_APP
        public async Task<Session> Finalize(WebAuthenticationBrokerContinuationEventArgs args)
        {
            Exception exception = null;
            try
            {
                return await GetSession(args.WebAuthenticationResult);
            }
            catch (Exception e)
            {
                exception = e;
            }

            await _logManager.LogAsync(exception);

            return null;
        }
#endif
        private async Task<Session> GetSession(WebAuthenticationResult result)
        {
            if (result.ResponseStatus == WebAuthenticationStatus.Success)
            {
                var code = GetCode(result.ResponseData);
                var serviceRequest = await GetToken(code);

                return new Session
                {
                    AccessToken = serviceRequest.access_token,
                    ExpireDate = new DateTime(long.Parse(serviceRequest.expires_in)),
                    Id = serviceRequest.id_token,
                    Provider = Constants.GoogleProvider
                };
            }
            if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
            {
                throw new Exception("Error http");
            }
            if (result.ResponseStatus == WebAuthenticationStatus.UserCancel)
            {
                throw new Exception("User Canceled.");
            }
            return null;
        }

        private static async Task<ServiceResponse> GetToken(string code)
        {
            
            const string TokenUrl = "https://accounts.google.com/o/oauth2/token";

            var body = new StringBuilder();
            body.Append(code);
            body.Append("&client_id=");
            body.Append(Uri.EscapeDataString(Constants.GoogleClientId));
            body.Append("&client_secret=");
            body.Append(Uri.EscapeDataString(Constants.GoogleClientSecret));
            body.Append("&redirect_uri=");
            body.Append(Uri.EscapeDataString(Constants.GoogleCallbackUrl));
            body.Append("&grant_type=authorization_code");
            
            var client = new HttpClient();
            var request = new HttpRequestMessage(HttpMethod.Post, new Uri(TokenUrl))
            {
                Content = new StringContent(body.ToString(), Encoding.UTF8, "application/x-www-form-urlencoded")
            };
            var response = await client.SendAsync(request);
            var content = await response.Content.ReadAsStringAsync();
         
            var serviceTequest = JsonConvert.DeserializeObject<ServiceResponse>(content);
            return serviceTequest;
        }
    }

 

Here, we don´t have anything new, this is similar to the FacebookService.

Note: FacebookService and GoogleService are similar but the response and request are different for the reason that these classes are not joined.

The MicrosoftService

/// <summary>   
/// The microsoft service.   
/// </summary>   
public class MicrosoftService : IMicrosoftService   
{   
    private readonly ILogManager _logManager;   
    private LiveAuthClient _authClient;   
    private LiveConnectSession _liveSession;   
  
  
    /// <summary>   
    /// Defines the scopes the application needs.   
    /// </summary>   
    private List<string> _scopes;   
       
    /// <summary>   
    /// Initializes a new instance of the <see cref="MicrosoftService"/> class.   
    /// </summary>   
    /// <param name="logManager">   
    /// The log manager.   
    /// </param>   
    public MicrosoftService(ILogManager logManager)   
    {   
        _scopes = new List<string> { "wl.signin", "wl.basic", "wl.offline_access" };   
        _logManager = logManager;   
    }   
  
    /// <summary>   
    /// The login async.   
    /// </summary>   
    /// <returns>   
    /// The <see cref="Task"/> object.   
    /// </returns>   
    public async Task<Session> LoginAsync()   
    {   
  
        Exception exception = null;   
        try   
        {   
            _authClient = new LiveAuthClient();   
            var loginResult = await _authClient.InitializeAsync(_scopes);   
            var result = await _authClient.LoginAsync(_scopes);   
            if (result.Status == LiveConnectSessionStatus.Connected)   
            {   
                _liveSession = loginResult.Session;   
                var session = new Session   
                {   
                    AccessToken = result.Session.AccessToken,   
                    Provider = Constants.MicrosoftProvider,   
                };   
  
                return session;   
            }   
  
        }   
        catch (LiveAuthException ex)   
        {   
            throw new InvalidOperationException("Login canceled.", ex);   
        }   
  
        catch (Exception e)   
        {   
            exception = e;   
        }   
         await _logManager.LogAsync(exception);   
  
        return null;   
    }   
  
    /// <summary>   
    /// The get user info.   
    /// </summary>   
    /// <returns>   
    /// The <see cref="Task"/> object.   
    /// </returns>   
    public async Task<IDictionary<string, object>> GetUserInfo()   
    {   
  
        Exception exception = null;   
        try   
        {      
            var liveClient = new LiveConnectClient(_liveSession);   
            LiveOperationResult operationResult = await liveClient.GetAsync("me");   
  
            return operationResult.Result;   
        }   
        catch (LiveConnectException e)   
        {   
            exception = e;   
        }   
        await _logManager.LogAsync(exception);   
  
        return null;   
    }   
  
    /// <summary>   
    /// The logout.   
    /// </summary>   
    public async void Logout()   
    {   
  
        if (_authClient == null)   
        {   
            _authClient = new LiveAuthClient();   
            var loginResult = await _authClient.InitializeAsync(_scopes);   
        }   
        if (_authClient.CanLogout)   
        {   
            _authClient.Logout();   
        }   
    }   
}

This class was reused from the sample Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM), but for it work is required to associate the app to the store.

That will result in an additional file in the project: as in the following:

Note

 

  1. These file are removed from the sample because each developer has their own.
  2. In devices that the Microsoft account is logged the application will do the login automatically.

The SessionService

/// <summary>   
/// The service session.   
/// </summary>   
public class SessionService : ISessionService   
{   
    private readonly IApplicationDataService _applicationSettings;   
    private readonly IFacebookService _facebookService;   
    private readonly IMicrosoftService _microsoftService;   
    private readonly IGoogleService _googleService;   
    private readonly ILogManager _logManager;   
       
    /// <summary>   
    /// Initializes a new instance of the <see cref="SessionService" /> class.   
    /// </summary>   
    /// <param name="applicationSettings">The application settings.</param>   
    /// <param name="facebookService">The facebook service.</param>   
    /// <param name="microsoftService">The microsoft service.</param>   
    /// <param name="googleService">The google service.</param>   
    /// <param name="logManager">The log manager.</param>   
    public SessionService(IApplicationDataService applicationSettings,   
        IFacebookService facebookService,   
        IMicrosoftService microsoftService,   
        IGoogleService googleService, ILogManager logManager)   
    {   
        _applicationSettings = applicationSettings;   
        _facebookService = facebookService;   
        _microsoftService = microsoftService;   
        _googleService = googleService;   
        _logManager = logManager;   
    }   
  
    /// <summary>   
    /// Gets the session.   
    /// </summary>   
    /// <returns>The session object.</returns>   
    public Session GetSession()   
    {   
        var expiryValue = DateTime.MinValue;   
        string expiryTicks = LoadEncryptedSettingValue("session_expiredate");   
        if (!string.IsNullOrWhiteSpace(expiryTicks))   
        {   
            long expiryTicksValue;   
            if (long.TryParse(expiryTicks, out expiryTicksValue))   
            {   
                expiryValue = new DateTime(expiryTicksValue);   
            }   
        }   
  
        var session = new Session   
        {   
            AccessToken = LoadEncryptedSettingValue("session_token"),   
            Id = LoadEncryptedSettingValue("session_id"),   
            ExpireDate = expiryValue,   
            Provider = LoadEncryptedSettingValue("session_provider")   
        };   
        _applicationSettings.LocalSettings[Constants.LoginToken] = true;   
        return session;   
    }   
  
    /// <summary>   
    /// The save session.   
    /// </summary>   
    /// <param name="session">   
    /// The session.   
    /// </param>   
    private void Save(Session session)   
    {   
        SaveEncryptedSettingValue("session_token", session.AccessToken);   
        SaveEncryptedSettingValue("session_id", session.Id);   
        SaveEncryptedSettingValue("session_expiredate", session.ExpireDate.Ticks.ToString(CultureInfo.InvariantCulture));   
        SaveEncryptedSettingValue("session_provider", session.Provider);   
        _applicationSettings.LocalSettings[Constants.LoginToken] = true;   
    }   
  
    /// <summary>   
    /// The clean session.   
    /// </summary>   
    private void CleanSession()   
    {   
        _applicationSettings.LocalSettings.Remove("session_token");   
        _applicationSettings.LocalSettings.Remove("session_id");   
        _applicationSettings.LocalSettings.Remove("session_expiredate");   
        _applicationSettings.LocalSettings.Remove("session_provider");   
        _applicationSettings.LocalSettings.Remove(Constants.LoginToken);   
    }   
  
    /// <summary>   
    /// The login async.   
    /// </summary>   
    /// <param name="provider">   
    /// The provider.   
    /// </param>   
    /// <returns>   
    /// The <see cref="Task"/> object.   
    /// </returns>   
    public async Task<bool?> LoginAsync(string provider)   
    {   
        Provider = provider;   
        Exception exception;   
        try   
        {   
            Session session = null;   
            switch (provider)   
            {   
                case Constants.FacebookProvider:   
                    session = await _facebookService.LoginAsync();   
                    break;   
                case Constants.MicrosoftProvider:   
                    session = await _microsoftService.LoginAsync();   
                    break;   
                case Constants.GoogleProvider:   
                    session = await _googleService.LoginAsync();   
                    break;   
            }   
            if (session == null)   
            {   
                return null;   
            }   
            Save(session);   
               
            return true;   
        }   
        catch (Exception ex)   
        {   
            exception = ex;   
        }   
        await _logManager.LogAsync(exception);   
  
        return false;   
    }   
  
    /// <summary>   
    /// Gets or sets the provider.   
    /// </summary>   
    /// <value>   
    /// The provider.   
    /// </value>   
    public string Provider { get; set; }   
  
    /// <summary>   
    /// The logout.   
    /// </summary>   
    public async void Logout()   
    {   
        Exception exception = null;   
        try   
        {   
            var session = GetSession();   
            switch (session.Provider)   
            {   
                case Constants.FacebookProvider:   
                    _facebookService.Logout();   
                    break;   
                case Constants.MicrosoftProvider:   
                    _microsoftService.Logout();   
                    break;   
                case Constants.GoogleProvider:   
                    _googleService.Logout();   
                    break;   
            }   
            CleanSession();   
        }   
        catch (Exception ex)   
        {   
            exception = ex;   
        }   
        if (exception != null)   
        {   
            await _logManager.LogAsync(exception);   
        }   
    }   
  
WINDOWS_PHONE_APP   
    public async Task<bool> Finalize(WebAuthenticationBrokerContinuationEventArgs args)   
    {   
        Exception exception = null;   
        try   
        {   
            Session session = null;   
            switch (Provider)   
            {   
                case Constants.FacebookProvider:   
                    session = await _facebookService.Finalize(args);   
                    break;   
                case Constants.GoogleProvider:   
                    session = await _googleService.Finalize(args);   
                    break;   
            }   
            Save(session);   
            return true;   
        }   
        catch (Exception e)   
        {   
            exception = e;   
        }   
        await _logManager.LogAsync(exception);   
        return false;   
    }   
if   
    /// <summary>   
    /// Loads an encrypted setting value for a given key.   
    /// </summary>   
    /// <param name="key">   
    /// The key to load.   
    /// </param>   
    /// <returns>   
    /// The value of the key.   
    /// </returns>   
    private string LoadEncryptedSettingValue(string key)   
    {   
        string value = null;   
  
        var protectedBytes = _applicationSettings.LocalSettings[key];   
        if (protectedBytes != null)   
        {   
            // todo use DataProtectionProvider   
            // byte[] valueBytes = ProtectedData.Unprotect(protectedBytes, null);   
            // value = Encoding.UTF8.GetString(valueBytes, 0, valueBytes.Length);   
            value = protectedBytes.ToString();   
        }   
  
        return value;   
    }   
  
    /// <summary>   
    /// Saves a setting value against a given key, encrypted.   
    /// </summary>   
    /// <param name="key">   
    /// The key to save against.   
    /// </param>   
    /// <param name="value">   
    /// The value to save against.   
    /// </param>   
    /// <exception cref="System.ArgumentOutOfRangeException">   
    /// The key or value provided is unexpected.   
    /// </exception>   
    private void SaveEncryptedSettingValue(string key, string value)   
    {   
        if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value))   
        {   
            // todo use DataProtectionProvider   
            // byte[] valueBytes = Encoding.UTF8.GetBytes(value);   
            // var dataProtectionProvider = new DataProtectionProvider();             
            // // Encrypt the value by using the Protect() method.   
            // byte[] protectedBytes = await dataProtectionProvider.ProtectAsync(valueBytes);   
            _applicationSettings.LocalSettings[key] = value;   
        }   
    }   
}

This class will return null if the session is null, we need to be aware that there isn´t any error and the session is returned or if it is null, the process will be done by the Finalize method.

Here is the class diagram with all classes and interfaces created in this sample:

Now to build the User Interface and because we are using the MVVM pattern, it created a LoginViewModel to do the binding to the LoginView.

The LoginViewModel

/// <summary>   
    /// The login view model.   
    /// </summary>   
    public class LoginViewModel : ViewModelBase   
    {   
        private readonly ILogManager _logManager;   
        private readonly IMessageBoxService _messageBox;   
        private readonly INetworkInformationService _networkInformationService;   
        private readonly INavigationService _navigationService;   
        private readonly ISessionService _sessionService;   
        private bool _inProgress;   
   
        /// <summary>   
        /// Initializes a new instance of the <see cref="LoginViewModel"/> class.   
        /// </summary>   
        /// <param name="navigationService">   
        /// The navigation service.   
        /// </param>   
        /// <param name="sessionService">   
        /// The session service.   
        /// </param>   
        /// <param name="messageBox">   
        /// The message box.   
        /// </param>   
        /// <param name="networkInformationService">   
        /// The network connection.   
        /// </param>   
        /// <param name="logManager">   
        /// The log manager.   
        /// </param>   
        public LoginViewModel(INavigationService navigationService,   
            ISessionService sessionService,   
            IMessageBoxService messageBox,   
            INetworkInformationService networkInformationService,   
            ILogManager logManager)   
        {   
            _navigationService = navigationService;   
            _sessionService = sessionService;   
            _messageBox = messageBox;   
            _networkInformationService = networkInformationService;   
   
            _logManager = logManager;   
            LoginCommand = new RelayCommand<string>(LoginAction);   
        }   
   
        /// <summary>   
        /// Gets the navigation service.   
        /// </summary>   
        /// <value>   
        /// The navigation service.   
        /// </value>   
        public INavigationService NavigationService   
        {   
            get { return _navigationService; }   
        }   
   
        /// <summary>   
        /// Gets or sets a value indicating whether in progress.   
        /// </summary>   
        /// <value>   
        /// The in progress.   
        /// </value>   
        public bool InProgress   
        {   
            get { return _inProgress; }   
            set { Set(() => InProgress, ref _inProgress, value); }   
        }   
   
        /// <summary>   
        /// Gets the facebook login command.   
        /// </summary>   
        /// <value>   
        /// The facebook login command.   
        /// </value>   
        public ICommand LoginCommand { get; private set; }   
   
        /// <summary>   
        /// Facebook's login action.   
        /// </summary>   
        /// <param name="provider">   
        /// The provider.   
        /// </param>   
        private async void LoginAction(string provider)   
        {   
            Exception exception = null;   
            bool isToShowMessage = false;   
            try   
            {   
                if (!_networkInformationService.IsNetworkAvailable)   
                {   
                    await _messageBox.ShowAsync("There isn´t network connection.",   
                                          "Authentication Sample",   
                                          new List<string> { "Ok" });   
                    return;   
                }   
                if (Constants.GoogleClientId.Contains("<") || Constants.GoogleClientSecret.Contains("<"))   
                {   
                    await _messageBox.ShowAsync("Is missing the google client id and client secret. Search for Constant.cs file.",   
                                         "Authentication Sample",   
                                         new List<string> { "Ok" });   
                    return;   
                }   
                if (Constants.FacebookAppId.Contains("<"))   
                {   
                    await _messageBox.ShowAsync("Is missing the facebook client id. Search for Constant.cs file.",   
                                         "Authentication Sample",   
                                         new List<string> { "Ok" });   
                    return;   
                }   
                InProgress = true;   
                var auth = await _sessionService.LoginAsync(provider);   
                if (auth == null)   
                {   
                    return;   
                }   
                if (!auth.Value)   
                {   
                    await ShowMessage();   
                }   
                else   
                {   
                    _navigationService.Navigate<MainView>();   
                    InProgress = false;   
                }   
   
                InProgress = false;   
            }   
            catch (Exception ex)   
            {   
                InProgress = false;   
                exception = ex;   
                isToShowMessage = true;   
            }   
            if (isToShowMessage)   
            {   
                await _messageBox.ShowAsync("Application fails.",   
                                           "Authentication Sample",    
                                            new List<string> { "Ok" });   
            }   
            if (exception != null)   
            {   
                await _logManager.LogAsync(exception);   
            }   
        }   
   
        private async Task ShowMessage()   
        {   
            await _messageBox.ShowAsync("Wasn´t possible to complete the login.",   
               "Authentication Sample",   
                new List<string>   
                {   
                   "Ok"    
                });   
        }   
  
#if WINDOWS_PHONE_APP   
        public async void Finalize(WebAuthenticationBrokerContinuationEventArgs args)   
        {   
            var result = await _sessionService.Finalize(args);   
            if (!result)   
            {   
                await ShowMessage();   
            }   
            else   
            {   
                _navigationService.Navigate<MainView>();   
                InProgress = false;   
            }   
        }   
#endif   
    }

Note: In the LoginAction the parameter provider is the value of the CommandParameter received in LoginCommand, this is set in the login page.

For the final the code, let’s see the code for the page.

The LoginPage.xaml

<Page   
    x:Class="AuthenticationSample.UniversalApps.Views.LoginView"   
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
    xmlns:converters="using:Cimbalino.Toolkit.Converters"   
    mc:Ignorable="d">   
   
    <Page.DataContext>   
        <Binding Mode="OneWay"   
                 Path="LoginViewModel"   
                 Source="{StaticResource Locator}" />   
    </Page.DataContext>   
    <Page.Resources>   
        <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>   
    </Page.Resources>   
    <!--  LayoutRoot is the root grid where all page content is placed  -->   
    <Grid x:Name="LayoutRoot" Background="LightGray">   
        <Grid.RowDefinitions>   
            <RowDefinition Height="{StaticResource HeaderHeigth}" />   
            <RowDefinition Height="*" />   
        </Grid.RowDefinitions>   
   
        <!--  TitlePanel contains the name of the application and page title  -->   
        <StackPanel x:Name="TitlePanel"    
                    Margin="{StaticResource HeaderMargin}"   
                    VerticalAlignment="Center" Grid.Row="0">   
            <TextBlock  FontSize="30"    
                        Foreground="Black"   
                       Text="Login"/>   
        </StackPanel>   
   
        <!--  ContentPanel - place additional content here  -->   
        <Grid x:Name="ContentPanel"   
              VerticalAlignment="Center"   
              HorizontalAlignment="Center"   
              Grid.Row="1">   
            <Grid.RowDefinitions>   
                <RowDefinition Height="Auto" />   
                <RowDefinition Height="80" />   
                <RowDefinition Height="80" />   
                <RowDefinition Height="80" />   
                <RowDefinition Height="80" />   
            </Grid.RowDefinitions>   
            <TextBlock Grid.Row="0"    
                       FontSize="20"   
                       Foreground="Black"   
                       Text="Use your account"/>   
            <Button Grid.Row="1" Width="300"   
                    Margin="10"   
                    Command="{Binding LoginCommand}"   
                    CommandParameter="facebook"   
                    Background="{StaticResource FacebookBlueBackgroundBrush}" >   
                <StackPanel Orientation="Horizontal">   
                    <TextBlock Text="ï‚š"    
                                   VerticalAlignment="Center"   
                                   FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"    
                                   HorizontalAlignment="Left"/>   
                    <TextBlock Margin="10,0,0,0" Text="Facebook"    
                           HorizontalAlignment="Center"/>   
                </StackPanel>   
            </Button>   
            <Button Grid.Row="3"   
                    Margin="10" Width="300"   
                    Command="{Binding LoginCommand}"   
                    CommandParameter="microsoft"   
                    Background="{StaticResource MicrosoftBlueBackgroundBrush}" >   
                <StackPanel Orientation="Horizontal">   
                    <TextBlock Text="ï…º"   
                                   VerticalAlignment="Center"   
                                   FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"    
                                   HorizontalAlignment="Left"/>   
                    <TextBlock Margin="10,0,0,0" Text="Microsoft"    
                           HorizontalAlignment="Center"/>   
                </StackPanel>   
            </Button>   
            <Button Grid.Row="2" Width="300"   
                    Margin="10"   
                    Command="{Binding LoginCommand}"   
                    CommandParameter="google"   
                    Background="{StaticResource GoogleRedBackgroundBrush}" >   
                <StackPanel Orientation="Horizontal">   
                    <TextBlock Text=""   
                                   VerticalAlignment="Center"   
                                   FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"    
                                   HorizontalAlignment="Left"/>   
                    <TextBlock Margin="10,0,0,0" Text="Google"    
                           HorizontalAlignment="Center"/>   
                </StackPanel>   
            </Button>   
        </Grid>   
        <Grid Visibility="{Binding InProgress, Converter={StaticResource BooleanToVisibilityConverter}}"   
            Grid.Row="0"   
                   Grid.RowSpan="2">   
            <Rectangle    
                   Fill="Black"   
                   Opacity="0.75" />   
            <TextBlock    
                   HorizontalAlignment="Center"   
                   VerticalAlignment="Center"   
                   Text="Auth..." />   
            <ProgressBar IsIndeterminate="True" IsEnabled="True" Margin="0,60,0,0"/>   
        </Grid>   
   
    </Grid>   
</Page>

For use in the same page in Windows 8.1 Store apps and Windows Phone 8.1 Runtime was used some style.

The LoginView.xaml.cs

/// <summary>   
    /// An empty page that can be used on its own or navigated to within a Frame.   
    /// </summary>   
#if WINDOWS_PHONE_APP   
       public sealed partial class LoginView : Page, IWebAuthenticationContinuable   
#else   
      public sealed partial class LoginView : Page   
#endif   
    {   
        /// <summary>   
        /// Initializes a new instance of the <see cref="LoginView"/> class.   
        /// </summary>   
        public LoginView()   
        {   
            InitializeComponent();   
        }   
   
        /// <summary>   
        /// The on navigated to.   
        /// </summary>   
        /// <param name="e">   
        /// The e.   
        /// </param>   
        protected override void OnNavigatedTo(NavigationEventArgs e)   
        {   
            var viewModel = (LoginViewModel)DataContext;   
            viewModel.NavigationService.RemoveBackEntry();   
            base.OnNavigatedTo(e);   
        }   
  
#if WINDOWS_PHONE_APP   
   
        /// <summary>   
        /// Continues the web authentication.   
        /// </summary>   
        /// <param name="args">The <see cref="Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs"/> instance containing the event data.</param>   
        public void ContinueWebAuthentication(Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs args)   
        {   
            var viewModel = (LoginViewModel)DataContext;   
            viewModel.Finalize(args);   
        }   
#endif   
    }

Conclusion

In conclusion, the flow is completely different for each platform and it has some impact in the development and in sharing the code. Maybe, for this reason there isn’t any package for Windows Phone 8.1 that uses authentication.

Source Code

The source code can be found here.

Source Code Files

  • IFacebookService interface for FacebookService
  • IGoogleService interface for GoogleService
  • ILogManager interface for LogManager
  • IMicrosoftService interface for MicrosoftService
  • ISessionProvider interface for all providers interface (common methods)
  • ISessionService for SessionService
  • Session class for save the information about the session (provider, token, expired date)
  • FacebookService class that has all logic about the login / logout using Facebook provider
  • GoogleService class that has all logic about the login / logout using Google provider
  • MicrosoftService class that has all logic about the login / logout using Microsoft provider
  • SessionService class for manage login/logout (it will use all services provides described before)
  • LoginViewModel class for binding to LoginView.xaml
  • LoginView class that represent the page for login
  • MainViewModel class for binding to MainView.xaml
  • MainView class that appear after the login is ok
  • ViewModelLocator class contains static references to all the view model in application and provides an entry point for the bindings.

The solution

Build the Sample

  1. Start Visual Studio Express 2012 for Windows 8 and select File > Open > Project/Solution.
  2. Go to the directory in which you unzipped the sample. Go to the directory named for the sample and double-click the Visual Studio Express 2012 for Windows 8 Solution (.sln) file.
  3. Press F7 or use Build > Build Solution to build the sample.

Note: you can use Visual Studio 2013 in Windows 8.1.

Run the sample

To debug the app and then run it, press F5 or use Debug > Start Debugging. To run the app without debugging, press Ctrl+F5 or use Debug > Start Without Debugging

Output

The output for each target is:

  • Windows 8.1 Store App

Login Page

Microsoft Authentication

Facebook Authentication

Google Authentication

  • Windows Phone 8.1 App

Login View

Facebook Authentication

Google Authentication

 

Interview with a Visual C# MVP Wiki Ninja – Sara Silva

Today was published my interview for Wiki Ninjas blog.

” Wiki Ninjas is a group of authors who celebrate and evangelize the community catalyst and social-authoring excellence that is called TechNet Wiki “

wiki ninjas

The full interview is here:

Interview with a Visual C# MVP Wiki Ninja – Sara Silva

My first experience about my new Nokia X

In the last week I received a Nokia X device, I know that the news about it are not goods, Microsoft kills off its Nokia Android phones, but it did not died, yet.

Normally I use Nokia Lumia 925 and 920 with Windows Phone 8.1 Dev Preview and the first moments with Nokia X was to do the same I usually do when I do a hard reset or work with a new device. The first thing I do is to set my Microsoft account for upload all my contacts, but I did not find a way to do this in Nokia X and was sad for me! Another thing was taken screenshots, I use this feature a lot, and in Nokia X is possible to take screenshots pressing Volum Down + Power button, really nice.

About the contacts I can do the basic operation and I can import/export contacts or connect to my accounts, but not find a way to connect Microsoft account, like I do in WP.
contacts

Here is a sample the accounts I connected
connected contacts

The information about my device is here

about phone

Is not the Nokia X2, but is nice for explore it.

 

The action center is similar to Windows Phone, but in Windows Phone I have more information and options that is very useful to quicky definitions.

action center

Nokia X allow to have a track about all operation in my device, interesting in some case like a download made from website and I did not where was saved.

recent

The lock screen is complete different from Windows Phone and I is updated constantly, for example I have some e-mail that I marked as unread and sometime the device tells me you have emails to read, even I visit the email app and quit. I did not liked it :)

notifications

 

Another thing I was strange for me, is related with email accounts. I setted two different emails accounts and would like to have two icons for it, but I only have one and in email I need to select each email account want to see, and the notification about the number the emails to read is relative to the email account selected… I prefer the Windows Phone way! :)

In this moment, I am using apps like facebook, twitter, skype, email, …and it has a similar experiencie as in Windows Phone.

In future, I want to try more apps and I want to see how easy is to port apps from Android to Nokia X and deploy from Xamarin.

Note: I installed an Android app that is in development and was easy, I installed only the apk! I did not need any developer account like in Windows Phone, was nice.

 

[TechNet Wiki] Windows Phone and Windows Store Apps Technical Guru – June 2014

Yesterday was published the The Microsoft TechNet Guru Awards! (June 2014) from TechNet Wiki and for the second consecutive month I won the

Windows Phone and Windows Store Apps Technical Guru - June 2014 

Technical Guru

 

I am really happy! :)

You can read my article here:

Creating Windows Phone and Window 8.1 applications using Microsoft App Studio