Wednesday, November 19, 2014

Universal Apps using the .NET client library for Google APIs

Hi all,

In the last couple weeks, Jesse and I improved the samples repository of the Google APIs client library for .NET with new samples for Windows and Windows Phone.
Jesse authored Translate.WP.TranslateText - a Windows Phone sample for the Google Translate API.
A week ago, I submitted a Blogger.Sample for Universal Store apps using the Google Blogger API.
These are the first samples in our repository that target Windows Phone 8.1 and Windows 8.1 apps using the .NET client library for Google APIs.

In this blog post I'll focus on the Universal Windows sample I created. The sample shares code between the Windows Store and Windows Phone versions, and uses MVVM best practices.

I must admit, the last time I wrote MVVM was back in 2011, back then I worked on a huge WPF project and while working on MVVM again I saw that nothing really has changed since then.

For those of you who don’t know, Universal Windows apps help you develop and share code between all Windows devices. You can read more about it in building-universal-windows-apps.
It's important to mention that I already had a hacky sample for Windows Phone 8.0 that I used to test a new release of the client library with it. I just thought that it is going to be easy to upgrade it to Windows Phone 8.1.

I WAS WRONG

I didn't find a right meme... any suggestion will be appreciated :)


Microsoft introduced significant breaking API changes when upgrading from 8.0 to 8.1. The WebAuthenticationBroker.AuthenticateAsync which I used in Windows Phone 8.0 isn't supported anymore. Starting with Windows 8.1, Microsoft supports several AndContinue methods (one of them is WebAuthenticationBroker.AuthenticateAndContinue). When you call an AndContinue method, the app is deactivated until the operation completes. You can read more in How to continue your Windows Phone Store app after calling an AndContinue method.

So, in order to support the OAuth 2.0 protocol for Windows Phone Store apps, I had to do the following:


Step 1

Create core classes that I’m going to move shortly to Google.Apis and Google.Apis.Auth new projects that will target Windows Phone 8.1. Those classes include the following:

  1. PasswordVaultDataStore
A Windows Phone implementation of IDataStore. It uses the platform PasswordVault. PasswordVault represents a credential locker and its content is private for each app and can’t be used by other apps or services in the phone.

  1. AuthorizationCodeBroker
An implementation of ICodeReceiver. The important method is ReceiveCodeAsync which should be called twice.
In the 1st time, the method calls WebAuthenticationBroker.AuthenticateAndContinue so that users would be able to authorize this app to access their personal resources.
In the 2nd time, it should be called after the app had already received the authorization code. The implementation counts on the developer to store the code response in the PasswordVaultDataStore using WebAuthResult.Name. (see changes in MainPage.xaml.cs later on…)

  1. AuthorizationCodeWPInstalledApp
An implementation of IAuthorizationCodeInstalledApp, that uses the standard AuthorizationCodeInstalledApp with the AuthorizationCodeBroker mentioned above.

  1. GoogleWebAuthorizationBroker
A helper class that puts everything together - the installed app and the password vault data store.


* All those classes are very similar to classes that we already have for supporting Windows Store applications or Windows Phone 8.0.


Step 2

In addition, each Windows Phone app that wants to support continuation should implement the following:

ContinuationManager

A custom and a very short implementation that targets authentication ONLY. The full implementation is available here: http://msdn.microsoft.com/en-us/library/dn631755.aspx. Take a look in this short version:
App.xaml.cs
The application starting point. The important method is OnActivated, which might be called after the authorization code was received. The implementation looks like the following:
MainPage.xaml.cs
The first time the users log in to the app, the app should be suspended and the users must authorizes the app in order to access their private resources. When the app is activated again, it calls the continuation manager which in turn calls the ContinueWebAuthentication method. I think that the method is self documented, so without any words, take a look:

The only piece that is still missing to you now, is the BlogsViewModel and the BloggerRepository. Those two classes (and several others) are shared between the Windows and Windows Phone projects.
BlogsViewModel is just the ViewModel of the MainPage. When the users request to get all their blogs, the GetBlogsCommand is called. Then GetBlogsAsync will be executed and in turn it will call the repository to retrieve all blogs.
And the BloggerRepository looks like the following:
* Note that the implementation of GoogleWebAuthorizationBroker is different for Windows and Windows Phone apps.
Take a look in the code sample at Blogger.Sample. You will find all the missing pieces there, like IBloggerRepository, BlogViewModel, PostViewModel, MainPage and other classes. Just remember to follow the README file in order to setup and run the app.


Upstate NY, Oct 2014


Stay tuned for further documentation - the Windows Phone 8.1 section is going to be refreshed soon.



We hope you find our new sample useful when writing Windows Phone 8.1 apps with Google Apis. Let us know if you have any questions in the comments of this post, on the client issue tracker or in Stack Overflow using the google-api-dotnet-client tag (Preferred).



Enjoy!
Eyal (upside down)

Sunday, August 3, 2014

Musical artist events rich list

Exactly 4 weeks have passed since I came back from Brazil!
It was one of the best trip EVER. The world cup was amazing and the atmosphere was so magnificent! Many football (or should I say soccer...) fans from all over the world gathered together in the land of football (Yes, Although the 7-1, Brazil was and it's still the land of football :))

6 games, 15 goals, many games in Copacabana's fan fest, beers and caipirinhas with Brazilians, Hollandians, Germans, Argentinians, and as a matter of fact, with who not :)
It was a CRAZY FESTIVAL!
Holland(5) - Spain(1), what an amazing game to start with! Salvador, June 2014
Belgium(1) - Russia(0). Maracana, Rio de Janeiro, June 2014 

And back to work...

It's time to update you about my contribution to Google Search. Besides developing the Google APIs client library for .NET, I have been working in the last months on several search features. One of the things that I'm really proud of, is the artist events rich list.
Just "Google" [lada gaga events][christina perri tour][piano guys concerts] and a cool rich list with all upcoming events is going to be displayed, as following:



As you can see, first we display the nearby events; Google already knows that I'm living in NYC, so events that are located close to me are being displayed first, and then all the others, sorted by date.

Take a look in getting your events to the knowledge graph video by my Google fellow, Justin Boyan.



And what about the Google APIs client library for .NET you are asking?
I'm working on targeting Windows 8.1 as well as all the other platforms we already support. Version 1.9.0 is coming soon...

Eyal

Sunday, June 1, 2014

1.8.2 is here :)

Hi all,
Several days ago I released a new version of the core Google APIs client library for .NET (version 1.8.2)

This release includes several bug fixes, such as fixing MaxUrlLengthInterceptor to not delete the last character of a URL and improving FileDataStore to support an absolute path folder.

The full issues list is available in the library's latest blogpost.

In addition, from this release we support token revocationUserCredential now contains a new RevokeTokenAsync method which revokes the current token.

Later on, you can reauthorize the user again by calling GoogleWebAuthorizationBroker's ReauthorizeAsync. By doing so, the user will have to reauthorize your application to use his or her private resources.

Take a look in the following snippet:

Note the following:
  1. You should already be familiar with lines 1-20. Those lines contain the creation of user credentials and a book service, and a sample request to this service.
  2. In line 23 there is a call to the new RovokeTokenAsync method. From this point the access token is invalid and it is removed form the data store.
  3. Then, any request using the current credentials will fail, because the access token was revoked (lines 25-33).
  4. In this point, the user can reauthorize the application again to access his or her private resources, by calling to ReauthorizeAsync (line 37).

While working on this release I encountered the following error over and over again while trying to compile a Windows Phone sample using the new 1.8.2 library:


Error 3 Cannot resolve reference assemblies. Please check the reference assemblies. Cannot resolve dependency to assembly 'Google.Apis, Version=1.8.1.31687, Culture=neutral, PublicKeyToken=null' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.

It took me a lot of time to understand what is the exact problem, and I finally found a solution in this blogpost: http://www.ideatoappster.com/how-to-overcome-windows-phone-8-xaml-compilation-errors/.
Thanks Mr. Gill :) 
All I had to do is to change the following line in the Windows Phone project file:
<ValidateXaml>true</ValidateXaml>
And set the validate XAML tag to false.
That's all.


SF view from the Golden Gate Bridge, May 2014

The countdown continues... In 10 days I'm going to arrive in Brazil and in 12 days I'm going to watch my first world cup game ever. And... what an amazing game to start with: Spain-Holland.
Bom dia amigos!

Saturday, March 29, 2014

Mission Accomplished

I'm really proud that the Google APIs client library for .NET had reached GA.
It's something that I worked on in my last year, and I'm so happy that this awesome library is finally GA.

Read more about the GA announcements in the following links:

I recommend all of you to visit our Getting Started page and also OAuth 2.0, Media Upload and Media Download, Batch Requests, Performance Tips, and also the APIs page which includes all the supported Google APIs.

Jay Leno and I, on the day of the announcement, March 2014 :)
As you can see, Jay Leno is a big fan of the library :)

And to be serious again,
I know that I still owe you an EF implementation of IDataStore.
As I'm currently working on another big feature of the client library, I think I'll postpone it for later on. Sorry...

Eyal

Wednesday, January 29, 2014

ASP.NET MVC With Google OpenID and OAuth 2.0

Several days ago I encountered Rick Anderson's great Create an ASP.NET MVC 5 App with Facebook and Google OAuth2 and OpenID Sign-on post.
While reading the blog post, it became apparent to me that there should be a similar example showing how to Sign-in with Google and access Google APIs within an MVC 5 application. I chose to write a simple sample to display the user's files form his or her Google Drive account using Google Drive API.

* All the code of this tutorial is available in https://github.com/peleyal/peleyal.git.

* UPDATE (Dec 2014) *
Google OpenID is deprecated please use OAuth 2.0 for accessing Google APIs.
More information about migrating OpenID to OAuth 2.0 is available at: https://developers.google.com/+/api/auth-migration.

Prerequisite:

The pre-requisites for this tutorial are the same as those in Rick's example.
First, install Visual Studio Express 2013 for Web or Visual Studio 2013.
Next, create a new ASP.NET MVC application (I chose to call my application GoogleApis.Sample.MVC) with the default individual user account.
Continue as described in the article. Make sure to uncomment the following line:  app.UseGoogleAuthentication();
It's located in ConfigureAuth method in App_Start\Startup.Auth.cs file)

Let's start:

1. Install NuGet packages



2. OAuth 2.0

Follow the OAuth 2.0 page for ASP.NET MVC applications. I'll repeat the key instructions in this section.

2.1 Create a new AppAuthFlowMetadata that inherits from FlowMetadata.

In my sample I added the following class to Controllers folder.

using System;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Drive.v2;
using Google.Apis.Util.Store;

namespace Google.Apis.Sample.MVC.Controllers
{
    public class AppAuthFlowMetadata : FlowMetadata
    {
        private static readonly IAuthorizationCodeFlow flow =
            new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
                {
                    ClientSecrets = new ClientSecrets
                    {
                        ClientId = "PUT_CLIENT_ID_HERE",
                        ClientSecret = "PUT_CLIENT_SECRET_HERE"
                    },
                    Scopes = new[] { DriveService.Scope.Drive },
                    DataStore = new FileDataStore("Google.Apis.Sample.MVC")
                });

        public override string GetUserId(Controller controller)
        {
            return controller.User.Identity.GetUserName();
        }

        public override IAuthorizationCodeFlow Flow
        {
            get { return flow; }
        }
    }
}

  • Make sure to replace the ClientID and ClientSecret values with the client secrets from your Google Developers Cloud project.
  • Note: In my next blogpost I'll share a new implementation of EFDataStore to add support for Entity Framework as a storage mechanism.
  • I used the Microsoft.AspNew.Identity.GetUserName() extension method to get the user identity. Because I've configured the application to use the Google flow, the identity returned from this object corresponds to the user who authenticated to this site.

2.2 Create your Auth callback


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Google.Apis.Sample.MVC.Controllers
{
    public class AuthCallbackController
            Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
    {
        protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
        {
            get { return new AppAuthFlowMetadata(); }
        }
    }
}

Later on, I'll create the service object for accessing the Google Drive API.

2.3 Register the Auth address into your Cloud project

Get the project URL from your project file inside VS2013. Then register that URI in your Google Developers Console project.



As you can see in the above images, I registered http://localhost:49244/AuthCallback/IndexAsync as a Redirect URI to my cloud project (Visual Studio picked 49244 port to my web application).

Make sure you also enable the Drive API in the APIs tab.

3. Get files from the DriveService

In this step we will create a DriveService instance and fetch all the user's files.

3.1 Create a model for Drive's File

Before we can retrieve information about the user's Drive files, we should specify a "local" model (It's just a best practice).

using System;

namespace Google.Apis.Sample.MVC.Models
{
    public class FileModel
    {
        public string Id { get; set; }
        public string Title getset; }
        public DateTime? CreatedDate getset; }
        public string DownloadUrl getset; }
    }
}

3.2 Add DriveAsync method to the HomeController

Start by adding the following using statements:

using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;

using Google.Apis.Sample.MVC.Models;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Download;
using Google.Apis.Drive.v2;
using Google.Apis.Services;

Next, add the following code to make an API call to Google Drive:

    [Authorize]
    public async Task<ActionResult> DriveAsync(CancellationToken cancellationToken)
    {
        ViewBag.Message = "Your drive page.";

        var result = await new AuthorizationCodeMvcApp(this, new AppAuthFlowMetadata()).
                AuthorizeAsync(cancellationToken);

        if (result.Credential == null)
            return new RedirectResult(result.RedirectUri);

        var driveService = new DriveService(new BaseClientService.Initializer
            {
                HttpClientInitializer = result.Credential,
                ApplicationName = "ASP.NET Google APIs MVC Sample"
            });

        var listReq = driveService.Files.List();
        listReq.Fields = "items/title,items/id,items/createdDate,items/downloadUrl,items/exportLinks";
        var list = await listReq.ExecuteAsync();
        var items = 
            (from file in list.Items
             select new FileModel
             {
                  Title = file.Title,
                  Id = file.Id,
                  CreatedDate = file.CreatedDate,
                  DownloadUrl = file.DownloadUrl ?? 
                                            (file.ExportLinks != null ? file.ExportLinks["application/pdf"] : null),
             }).OrderBy(f => f.Title).ToList();
        return View(items);
    }

  • Notice that the DriveAsync operation is marked with the Authorize attribute. In this case we use this attribute because we require users to log in before they granted access to this content.
  • The result of AuthorizationCodeMvcApp.AuthorizeAsync is an AuthResult. The RedirectUri property is set in case the end-user needs to authorize. Otherwise the Credential property will be set with the user's credential.
  • Then I create the service and it's list request. Notice that I set the Fields property of the request to fetch only the fields that I'm going to use in the view.
  • I convert the Drive API file model into my sample model, order the list by title, and send it to the client. 

4. UI changes

4.1 Add a new DriveAsync view under Views/Home


@model IEnumerable<Google.Apis.Sample.MVC.Models.FileModel>

@{
    ViewBag.Title = "Drive";
}

<table>
    <tr>
        <th>
            Title
        </th>
        <th>
            Created Date
        </th>
        <th>
        </th>
    </tr>
    @foreach (var file in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => file.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => file.CreatedDate)
            </td>
            <td>
                @Html.ActionLink("Download", "DownloadAsync", new 
                     { 
                         title = file.Title,
                         downloadUrl = file.DownloadUrl 
                     })
            </td>
        </tr>
    }
</table>

4.2 Change _Layout.cshtml to include a link to the new page


                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Drive", "DriveAsync", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>

Add only the highlighted line. All the rest lines of code are already exist in the file.

5. Run


The first page you are going to see when running the web application is the following:


To sign in, click on the Drive tab. The following screen shows he login page.


Choose Google as a service login.


Now you should login.


Approve the site to use the required scopes to view your email address and basic information.


Choose a user name.


The above screen might look weird to you.
You already approved the application to use your email and basic information, so you might wonder why do you need to approve again other scopes?

The reason you need to approve the other scopes when the user sign in the first time is to authenticate the user. In the above screen the user needs to authorize this site to access his or her Drive data.

We might want to avoid those duplicate steps in the future and try making both of them as a one step, but I'll leave it to the future.


We are DONE!

6. Coming Next
  • Download a file using MediaDownloader
  • Implement a new EFDataStore - Entity Framework implementation of IDataStore.
  • Any suggestions?

And I know that you are curious about the photo of this post... It's Prague! And unfortunately not in a sunny day.

Prague Oct 2009