Mobile Zone
Mobile Zone is brought to you in partnership with:
spacer
spacer spacer Den D.
  • Website

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

Building an Imgur Client for Windows Phone - Part 1 - Core & Main Gallery

01.07.2013
| 6652 views |
  • Tweet
  • spacer

Related MicroZone Resources

The 2014 NoSQL Benchmark Showdown

NoSQL Performance When Scaling by RAM

Why NoSQL?

Free Intel Software Tech Webinars: C/C++/Fortran. See abstracts and register

NoSQL Evaluators Guide

Like this piece? Share it with your friends:

| More

Windows Phone does not yet have a comprehensive Imgur client, so I thought about developing one, that will provide a great user experience when it comes to browsing and sharing images through the service. This article series documents the development process from A to Z, showing how to use the API and how to represent the returned data in an efficient way in the application itself.

Getting Service Approval

First of all, you need to make sure that your application can access the Imgur endpoints. To do that, you need to sign up for a free account and register your application. This process is free for non-commercial applications, which means that you will not:

  • distribute your app as a paid product
  • display in-app advertisements

There is a way to do this for commercial applications through a paid endpoint, which I am not going to focus on in this series. Once you register the application, you will have two values in your hands - the client ID and the client secret.

spacer

The interesting part is that even if your applications accesses public data, such as the images from the main gallery, it still needs to send the authorization header with the client ID, in order to ensure that each application is respecting it's API rate limits.

The default rate limits are set to:

  • 1,250 uploads per day or
  • ~12,500 requests per day

This is more than enough for a simple application, and there is a way to keep track of how much your app has left from the quota, which I will be discussing in one of the next articles.

Starting Building The App

Open Visual Studio and create a new Windows Phone application. We will start with a 7.1 app, and will later extend it with capabilities from 8.0, so make sure you select the correct target operating system.

First and foremost, we need to create the code core - the Imgur service client. To do this, I created a new folder in my solution called ImgurAPI. Inside it, I create a class named ImgurClient - this will be the central connection point for any requests ran against the Imgur API.

spacer

As I mentioned that there are two constants for your app - the client ID and the client secret, you need to somehow tie those to the ImgurClient class. I've done this through a constructor:

private string _clientID;
private string _clientSecret;

public ImgurClient(string clientID, string clientSecret)
{
    _clientID = clientID;
    _clientSecret = clientSecret;
}

This way, a single instance can be used for the entire application. Now let's look at how we can get images from the main gallery. The main gallery is the public image repository. When users upload a public image, it is being placed in the main gallery. You can sort images by the time added and by how popular those are, so what you see by default is the top stack.

Look at the Gallery Image model to see what data you will get about each image returned after successfully executing the request. Based on the available description, I built a C# class that will represent the entity:

public class ImgurImage
{
  public string ID { get; set; }
  public string Title { get; set; }
  public Int64 DateTime { get; set; }
  public string Type { get; set; }
  [JsonProperty(PropertyName = "animated")]
  public bool IsAnimated { get; set; }
  public int Width { get; set; }
  public int Height { get; set; }
  public Int64 Size { get; set; }
  public Int64 Views { get; set; }
  [JsonProperty(PropertyName = "account_url")]
  public string AccountUrl { get; set; }
  public string Link { get; set; }
  public string Bandwidth { get; set; }
  public int Ups { get; set; }
  public int Downs { get; set; }
  public int Score { get; set; }
  [JsonProperty(PropertyName = "is_album")]
  public bool IsAlbum { get; set; }
}

There are a few things to mention here. You probably noticed that in the official description, some of the data types for properties such as DateTime are marked as integers. What the documentation doesn't tell you is what kind of integer is necessary. For example, for DateTime, a standard integer (Int32) would not suffice and you need to use Int64 (long) instead. Not doing so will result in an overflow exception.

Also some of the properties are marked with a JsonProperty attribute. I am using Json.NET for JSON data handling and deserialization. Add it to the project by right clicking on References in Solution Explorer and selecting Manage NuGet Packages.

spacer

By default, the JSON deserializer will associate each property with a field that has the same name in the JSON string. Having is_album as a C# property is not something commonly used, so that's where I can include an attribute that will override the default link between the field and property names.

Let's implement a function in the ImgurClient class that will retrieve the JSON data and will return it to the invoker.

/// <summary>
/// Get the images from the main gallery.
/// This call DOES NOT require authentcation.
/// </summary>
/// <param name="section"></param>
/// <param name="sort"></param>
/// <param name="page"></param>
public void GetMainGalleryImages(ImgurGallerySection section, ImgurGallerySort sort, int page, 
    Action<ImgurImageData> onCompletion)
{
    string _sort = sort.ToString().ToLower();
    string _section = section.ToString().ToLower();
    
    WebClient client = new WebClient();
    client.Headers["Authorization"] = "Client-ID " + _clientID;

    client.DownloadStringAsync(new Uri(string.Format(ImgurEndpoints.MainGallery, _section, _sort, page)));
    client.DownloadStringCompleted += (c, s) =>
    {
        var imageData = JsonConvert.DeserializeObject<ImgurImageData>(s.Result);
        onCompletion(imageData);
    };
}

As I mentioned previously, even for public data I need to set an authorization header that will tell Imgur what application is trying to aggregate data from the catalog. The main gallery endpoint is obtained from a static ImgurEndpoints class.

public static class ImgurEndpoints
{
    public const string MainGallery = "https://api.imgur.com/3/gallery/{0}/{1}/{2}.json";
}

When I am deserializing the data, I am getting an ImgurImageData object instead of a generic list. You might ask - why is that? Take a look at the result that you are getting (raw). A great tool to format JSON is JSON Formatter & Validator.

spacer

What's wrong with this JSON? Per se, nothing, but in order for me to deserialize a string to a List<ImgurImage>, I would need to have a raw JSON array. Here, I don't have one, but I have a data container. That's why there is an ImgurImageData class:

public class ImgurImageData
{
    [JsonProperty(PropertyName = "data")]
    public IEnumerable<ImgurImage> Images { get; set; }
}

Again, because I am not using the default name-to-name deserialization association, I am overriding the name of the field that is being used. To test the function, go to the main application page and use this snippet:

ImgurClient client = new ImgurClient(ConstantContainer.IMGUR_CLIENT_ID,
    ConstantContainer.IMGUR_CLIENT_SECRET);

client.GetMainGalleryImages(ImgurGallerySection.Hot, ImgurGallerySort.Viral, 0, (s) =>
    {
        ImgurImageData data = s;
        Debug.WriteLine(s.Images.First().AccountUrl);
    });

ConstantContainer is a static class that contains the pre-defined client ID and client secret. If you set a breakpoint at the Debug.WriteLine line, you will get a nice view of what's in the resulting container:

spacer

It's looking good so far, but I want to actually display the images somewhere in the application, and not just visualize their models in Visual Studio. To do this, I am actually going to move the ImgurClient instance to App.xaml.cs.

spacer

The data has to be inserted somewhere, obviously, so that's why I created a ViewModels folder in my solution, with a MainPageViewModel class inside it - this will be used to bind all data that needs to be handled on the main application page, and the images from the main gallery are a part of that.

using Imagine.ImgurAPI;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace Imagine
{
    public class MainPageViewModel : INotifyPropertyChanged
    {

        static MainPageViewModel instance = null;
        static readonly object padlock = new object();

        public MainPageViewModel()
        {
            HomeImages = new ObservableCollection<ImgurImage>();
        }

        public static MainPageViewModel Instance
        {
            get
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new MainPageViewModel();
                    }
                    return instance;
                }
            }
        }

        private ObservableCollection<ImgurImage> _homeImages;
        public ObservableCollection<ImgurImage> HomeImages
        {
            get
            {
                return _homeImages;
            }
            set
            {
                if (_homeImages != value)
                {
                    _homeImages = value;
                    NotifyPropertyChanged("HomeImages");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                System.Windows.Deployment.Current.Dispatcher.BeginInvoke(
                    () =>
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(info));
                    });
            }
        }
    }
}

This is a view model implementation I talked about here. Since there is more than one view in the application, it makes sense to have the view models separated - that way as a developer you won't have much problems debugging possible data misplacement and errors.

Back in MainPage.xaml.cs, make sure that you add a MainPage_Loaded event handler. Inside it, you can insert the main gallery image loading code:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    if ((MainPageViewModel.Instance.HomeImages == null) !=
        (MainPageViewModel.Instance.HomeImages.Count == 0))
    {
        App.ServiceClient.GetMainGalleryImages(ImgurGallerySection.Hot, ImgurGallerySort.Viral, 0, (s) =>
        {
            ImgurImageData data = s;
            MainPageViewModel.Instance.HomeImages =
                new System.Collections.ObjectModel.ObservableCollection<ImgurImage>(s.Images);
            System.Diagnostics.Debug.WriteLine("Main gallery images loaded in MainViewModel.");
        });
    }
}

Great, so now we have a collection that keeps the most up-to-date images from the main public Imgur gallery, however those are never displayed anywhere. Let's fix this. Open App.xaml and add a new namespace, pointing to the ViewModels folder:

xmlns:vms="clr-namespace:Imagine.ViewModels"

Inside the Application.Resources node, add a new node for the MainPageViewModel. Give it a unique key, as it will be referenced for binding:

<!--Application Resources-->
<Application.Resources>
    <vms:MainPageViewModel x:Key="MainPageViewModel"></vms:MainPageViewModel>
</Application.Resources>

Now we can do something with the underlying Images collection. In MainPage.xaml, add a new ListBox. For testing purposes, we can make it as simple as possible, with an image as the default ItemTemplate.

<ListBox ItemsSource="{Binding Path=Instance.HomeImages,
    Source={StaticResource MainPageViewModel}}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Image   Source="{Binding Link}"></Image>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

The results will be OK:

spacer

Settling for OK is not interesting enough, so let's work with a WrapPanel, the images being displayed in small adjacent squares, taking the most of the screen estate. The control is not part of the stock SDK, so you will need to add the WP Toolkit package through NuGet:

spacer

Add a reference to the new namespace in MainPage.xaml:

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;asse


gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.