Amazon Mobile: Book Shopping from your Smartphone

  This article walks the reader through the development of a Windows Mobile 5.0 application that interacts with Amazon's E-Commerce Service (ECS). I cover set up of the development environment and much of the code that is needed. Readers can download the C# or VB.NET source code that accompanies the article.
Clarity Consulting

Difficulty: Intermediate
Time Required: 3-6 hours
Cost: Greater than $200
Software: ActiveSync 4.2 .NET Compact Framework Windows Mobile 5.0 SDK
Hardware: Motorola Q
Download:

Introduction

In this article I'll examine part of Amazon's web service functionality and use the new Pocket Outlook API to create a useful application for the Windows Mobile 5.0 Smartphone. The current version of the application lets the user search Amazon's inventory, view book details, create and edit a cart, and checkout from the device. A Pocket Outlook task is used to store the cart's checkout link.

Development Environment

I wrote Amazon Mobile using Visual Studio 2005 Professional and a Motorola Q Smartphone running Windows Mobile 5.0. Windows Mobile devices use Microsoft's ActiveSync software to synchronize with a desktop, so you'll need to have it installed to set up the environment on the phone. Next you'll need the .NET Compact Framework, since this application is written in managed code.  Lastly, download the appropriate software development kit (SDK) for your platform. I installed the Windows Mobile 5.0 SDK for Smartphone because I am writing code for the Q. If you are using a Pocket PC device you should download and install the Pocket PC version. The rest of this article will assume that you are writing a Smartphone application, but if that is not the case you will find it easy enough to follow along using a Pocket PC device.

Readers who are not familiar with Amazon's web service may find it helpful to review Peter Bernhardt's introduction in this previous Coding4Fun article. I used ECS version 4.0 for this project. You won't have to download an SDK as in version 3.0, but you will need to create an account with Amazon to get a unique access key. Make sure to replace the “Your Access Key Here” string in Constants.cs with your own access key before running my code.

Once you've set up your dev environment, start by creating a new project for your application. Select New > Project from Visual Studio's File menu. You'll find the Device Application template under Visual C# > Smart Device > Windows Mobile 5.0 Smartphone in the Project Types tree on the left side of the New Project dialog.

Figure 1: The New Project dialog.

Visual Studio will use the default resolution and DPI settings of the current “form factor” to create the form you see in the designer. If you open a project written for a device with a 320x240 screen (such as the project I wrote for this article) before changing the settings, the IDE will move or resize the contents of the forms to make them fit the smaller 176x220 resolution. You can prevent this by changing the designer's form factor to match that of the Q. Choose Tools > Options, and then expand “Device Tools” in the tree on the left side of the dialog and select “Form Factors.” Find “Windows Mobile 5.0 Smartphone” in the menu and edit its properties. Clear the “Enable rotation support” checkbox and make sure that both the horizontal and vertical resolutions are set to 131 pixels per inch. Now clear the “Show Skin” checkbox and enter 320 for the screen width and 240 for the screen height. You can save this form factor with a new name, such as “MotoQ,” and make it your default if you prefer.

Writing the Application

Now let's get started in the code. First you'll need to add a reference to Amazon's Web Services Definition (Description) Language (WSDL). The web reference wizard will use the WSDL to create a proxy class that you'll use to access the service's functionality. Right-click on “Web References” in Solution Explorer, select “Add Web Reference” and enter the address of the WSDL as shown in the figure below.

Figure 2: Feel free to change the unwieldy name that Visual Studio assigns your Web reference.

Visual Studio has created a startup form that, by default, is the entry and exit point for your application. This form is a great place to accept search criteria from the user and display the results you get back from the web service. Find the Toolbox and add a TextBox control to your form to capture the user's input. You'll also need a ListView control to display the results. The softkey menu that's built into the default Smartphone form is a convenient way to let the user start the search. Add a MenuItem and handle its click event to call the search method you're about to write.

Figure 3: The SearchForm.

Make sure you've set a reference to the namespace created by the WSDL wizard in a using statement at the top of your code. To perform a keyword search against Amazon's product database, start by creating an ItemSearchRequest object and setting a few of its properties. ItemSearchRequest is declared as a partial class in the web reference's Reference.cs file, along with all of the other ECS classes. Here is the code I used to set up the search:

Imports AmazonMobile.com.amazon.webservices

...

       Private Sub PerformSearch(ByVal searchText As String)
            Dim aws As New AWSECommerceService()

            ' set up the request
            Dim itemSearchRequest As New ItemSearchRequest()
            itemSearchRequest.Keywords = searchText
            itemSearchRequest.SearchIndex = "Books"
            itemSearchRequest.ResponseGroup = New String() {"ItemAttributes"}
            itemSearchRequest.ItemPage = "1"

            Dim itemSearch As New ItemSearch()
            itemSearch.Request = New ItemSearchRequest(0) {itemSearchRequest}
            itemSearch.SubscriptionId = Constants.AMAZON_ACCESS_KEY
 
using AmazonMobile.com.amazon.webservices;

...
private void PerformSearch(string searchText)
{
    Cursor.Current = Cursors.WaitCursor;
    
    // clear out old results
    resultsView.Clear();

    AWSECommerceService aws = new AWSECommerceService();
    
    // set up the request
    ItemSearchRequest itemSearchRequest = new ItemSearchRequest();
    itemSearchRequest.Keywords = searchText;
    itemSearchRequest.SearchIndex = "Books";
    itemSearchRequest.ResponseGroup = new string[] { "ItemAttributes" };
    itemSearchRequest.ItemPage = "1";
    
    ItemSearch itemSearch = new ItemSearch();
    itemSearch.Request = new ItemSearchRequest[1] { itemSearchRequest };
    itemSearch.SubscriptionId = Constants.AMAZON_ACCESS_KEY;

Notice that I've set the ResponseGroup property to a string array containing the string “ItemAttributes.” The web service uses the values in this array to determine the subset of fields it should populate in the response object. You'll find a list of all possible options in the web service's documentation, a helpful guide to the functionality that the service exposes. Now you can ask the web service to perform the search.

// perform the search
ItemSearchResponse itemSearchResponse = aws.ItemSearch(itemSearch);
Items[] itemsResponse = itemSearchResponse.Items;
'perform the search
Dim response As ItemSearchResponse = aws.ItemSearch(itemSearch)
Dim itemsResponse As Items() = response.Items

Almost every interaction with ECS will follow the straightforward pattern you've just seen. First, create and set the properties of one or more request objects. Next, create the appropriate search object and use its properties to wrap one or more request objects, as well as your ECS access key. Finally, pass the search object to the appropriate web service method. The results of your request will be encapsulated by the object that is returned from the web service. You'll use the response object's properties to access the information that you want to display. Loop through the response object's Item collection, adding a new ListViewItem to the ListView's Items collection for each of the results. You can use the ListViewItem's Tag property to store the item's Amazon Standard Identification Number (ASIN) for future reference. In most cases, a book's ASIN is simply its ISBN.

The next step is creating a form to lookup any offers that might be associated with a search result. Right-click on the project node in Solution Explorer and select Add > Windows Form from the context menu. Give your form a name and click “Add” to create it in your project. For the offer form you'll probably want a ListView control to display the results of your lookup operation, as well as a few menu items at the bottom of the form for navigation. If you add a back button to the menu to return to the previous form the button's event handler should call the form's Close method.

In my code I construct an OfferForm using an ASIN string, which simply sets an instance variable to track the book associated with the current instance of the form. Your application will use a book's ASIN to lookup offers. You already know the item's ASIN, so you should create an ItemLookupRequest, in this case, instead of the ItemSearchRequest that you used to perform the initial search. You'll need to set the properties of one or more of the request objects and then set the ItemLookup's Request property as I did above. Finally, call the ItemLookup method on your instance of the web service and use the resulting ItemLookupResponse object to populate the ListView object. I suggest doing all this in a method that can be called by the form's Load event handler rather than the constructor. That way you'll avoid any conflicts that might occur if the form has not finished initializing its controls when you begin setting their properties.

Next you need a method to create a cart when the user selects an offer and since you don't want to create a new cart every time, you'll also want to be able to add an offer to a cart that already exists. A mechanism for writing information about a cart to the file system would be helpful. These are the functions of the Settings and SettingsManager classes that I've created to store the cart's ID and HMAC key. Take a look at the OnAddItem, CreateCart and GetCart methods to see how I implemented add and edit operations for the cart.

Amazon cart response objects have a PurchaseURL property that contains a URL string used to access the cart's checkout page. Once you have a cart with at least one item you should provide the user with access to this URL. I wanted to make use of the new Pocket Outlook API, so I added a Checkout method that stores the cart's purchase URL in the Body property of a new Pocket Outlook task. The Task class is part of the Microsoft.WindowsMobile.PocketOutlook namespace, so you'll need to add another reference to your project. Once you've done so, creating and adding a new task is simple:

Imports Microsoft.WindowsMobile.PocketOutlook

...
        Private Sub Checkout()
            ' warn the user that the cart will no longer be accessible
            If MessageBox.Show(Constants.MESSAGE_CHECKOUT_CONFIRMATION, Constants.CAPTION_CHECKOUT, MessageBoxButtons.OKCancel, MessageBoxIcon.None, MessageBoxDefaultButton.Button1) = Windows.Forms.DialogResult.OK Then
                ' Outlook Session implements IDisposable...
                Dim session As New OutlookSession()
                Try
                    Dim task As New Task()
                    task.Body = String.Format(Constants.MESSAGE_TASK_BODY, _purchaseUrl)
                    task.Subject = String.Format("{0}, {1}", Constants.CAPTION_CHECKOUT, DateTime.Now.ToLocalTime())
                    task.Complete = False
                    session.Tasks.Items.Add(task)
                Finally
                    session.Dispose()
                End Try

                ' clear the contents of the local cart
                LocalCartReset()
                ' reset the user interface
                OnCartEmpty(Constants.MESSAGE_CHECKOUT_COMPLETED)
            End If
        End Sub ' Checkout 
 
using Microsoft.WindowsMobile.PocketOutlook; 

… 

private void Checkout() 
{ 
    // warn the user that the cart will no longer be accessible 
    if (DialogResult.OK == MessageBox.Show(Constants.MESSAGE_CHECKOUT_CONFIRMATION,
            Constants.CAPTION_CHECKOUT, MessageBoxButtons.OKCancel, MessageBoxIcon.None, 
            MessageBoxDefaultButton.Button1)) 
    { 

        // Outlook Session implements IDisposable 
        using (OutlookSession session = new OutlookSession()) 
        {
            Task task = new Task(); 
            
            task.Body = string.Format(Constants.MESSAGE_TASK_BODY, _purchaseUrl); 
            task.Subject = string.Format("{0}, {1}", Constants.CAPTION_CHECKOUT, DateTime.Now); 
            task.Complete = false; 
            
            session.Tasks.Items.Add(task); 
        } 

        // clear the contents of the local cart 
        LocalCartReset(); 

        // reset the user interface 
        OnCartEmpty(Constants.MESSAGE_CHECKOUT_COMPLETED); 
    } 
} 

I reset the local cart's contents after storing the task because Amazon recommends that a cart no longer be used once a purchase is submitted. I don't know when my user will complete the transaction, so I make sure that my application can't access the cart again by clearing any saved information. It's worth noting that, for performance reasons, Microsoft suggests reusing the OutlookSession object for the life of the application rather than taking the above approach. My code has infrequent need of the task collection, however, so I've chosen to release the session's resources instead.

Once you have written the code to create a cart and edit its contents you should go back to your search form and handle the ListView's ItemActivate event. When someone selects an item from the list, your event handler should instantiate a new form to lookup offers for the item. In this case, it is best to do this modally, which means that execution in the search form halts until the OfferForm has been closed. Instantiate a new OfferForm and call ShowDialog:

using (OfferForm offerForm = new OfferForm(_asin)) 
{ 
    // the next line of code shows the OfferForm modally 
    offerForm.ShowDialog(); 
} 
 
Using offerForm As OfferForm = New OfferForm(_asin)
     offerForm.ShowDialog()
End Using

When the OfferForm is closed the search form will exit the using statement, calling the Dispose method on the OfferForm to release its resources.

Debugging

If you have a Motorola Q or any other Windows Mobile 5.0 Smartphone with a landscape resolution, debugging your code is as simple as connecting the device and then selecting “Windows Mobile 5.0 Smartphone Device” from the dropdown in the Debugging toolbar. When you start debugging, the IDE will deploy both your code and, depending on your configuration, the .NET Compact Framework to the device through ActiveSync.

If you don't have the hardware you want to use yet, don't worry. You can use an emulator to create a virtual device for testing your code. The emulator that ships with Visual Studio supports only portrait Smartphone layout, however, so the first thing you'll need is Microsoft's Emulator Image for Windows Mobile 5.0 Smartphone with 320x240 (Landscape) Screen. Next you'll want to download the Q skin plug-in, which you should be able to find at www.motocoder.com, and you may find the MOTO Q Developer Guide useful for the installation. Your emulator will not have a network connection when first installed but this post will help you get one configured. Select your emulator from the Debug toolbar dropdown and press F5 when you're ready to debug.

 

Figure 4: Amazon Mobile in action on the Q emulator.

Conclusion

As we've seen, the .NET Compact Framework makes distributed applications such as this one as easy to write for the mobile device as they are for the desktop. With much of the processing and storage done server-side, the scaled down processor and memory of the phone aren't a problem for most applications. Check back soon – my next version (v2.0) of Amazon Mobile will hopefully feature the device's camera as an ISBN scanner using the framework's new CameraCaptureDialog class.

Tags:

Follow the Discussion

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.