Windows Phone - Programming :Picasa Image Gallery with JSON in Window Phone
Picasa Image Gallery with JSON in Window Phone
This article explains how to create a Picasa Image Gallery with JSON in Windows Phone. We create an application called MyPicasa to illustrate this.
Introduction
This article is intends to show how one can access image albums (private/public/shared) in Google Picasa. Though there are several ways offered from Google to get image album data, "JSON" is covered in this article for fetching data.
MyPicasa application first authenticates to Google and get image albums data with JSON string (image 1). All the image album covers and title texts are displayed in vertically scrollable ListBox (image 2). Selected album's thumbnail images are loaded and displayed when user selects album from ListBox (image 3). Selected album images can be shown in portrait or landscape mode. User can show next or previous image using with Gestures (image 4).
Here is a small demo video, which shows how the application works:
Windows Phone SDK
To Develop application for Windows Phone devices, you need to install Windows Phone SDK(s),which can be downloaded from here
Windows Phone Application
To start creating a new Windows Phone application, start Microsoft Visual Studio then create a new Project and select Windows Phone Application Template.
In this example we have used C# as code behind language.
Project extensions
Microsoft Visual Studio extensions can be installed in many different ways. In this article we will use Nuget which makes extension installation easier. In this article we will need Silverlight Toolkit and Simple JSON extensions.
Nuget
"NuGet is a Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects that use the .NET Framework. When you add a library or tool, NuGet copies files to your solution and automatically makes whatever changes are needed in your project, such as adding references and changing your app.config or web.config file. When you remove a library, NuGet removes files and reverses whatever changes it made in your project so that no clutter is left."
More information about Nuget can be found here (and installation package).
Silverlight Toolkit
Silverlight Toolkit has many great extensions to Windows Phone developers. In this article we will use WrapPanel Control to layout Album thumbnails to the screen. You can add Silverlight Toolkit to your project with Nuget in Visual Studio:
Select: Tools > Library Package Manager... > Manage Nuget Packages for Solutions... and search Silverlight Toolkit
More information to Silverlight Toolkit can be found here.
Simple JSON
Here in Nokia Wiki there are quite a lot of coding examples covered with XML data in WP. This time we want to make an example with JSON. One possible extension is to use Simple JSON to handle JSON data from Google. You can add Simple JSON to your project with Nuget:
Select: Tools > Library Package Manager... > Manage Nuget Packages for Solutions... and search SimpleJSON
More information to Simple JSON can be found here.
Loading and displaying many images from net
There can be a small issue when you try to load lot of images from the web at the same time in Windows Phone. You can find a few assemblies from the net to fix this. One good one is PhonePerformance assembly (you can download compiled PhonePerformance assembly with source codes here). You have to download and unzip it. CopyPhonePerformance.dll to your project and add a new reference to it with Solution Explorer.
You might get warning asking you to unblock PhonePerformance.dll assembly (because it is downloaded from web). Close your Visual Studio. Open Windows Explorer and browse your PhonePerformance.dll file. Open file properties and Unblock.
Collection classes used in this code example
There are a lot of different kind of data available in Picasa, only the following data is used with albums and images in this code example.
Album.cs
Album class is used to store album information: title, published time, href to album and cover thumbnail.
To create New Classes in your project:
Right click your project in Solutions Explorer
Select Add and then Class...
namespace MyPicasa
{publicclass Album
{publicstring title {get;set;}publicstring published {get;set;}publicstring href {get;set;}publicstring thumbnail {get;set;}}}
AlbumImage.cs
AlbumImage class is used to store image information like image title, content url, size and thumbnail url.
User can give login information (email and password) in Login Page. Google authentication is checked with these information. If the optional username TextBlock is empty, then user's own public and private albums are loaded. User can add his/her friend username here to get shared albums downloaded instead.
OnNavigatedTo()-method will be called when this page is displayed. Here we first load stored email and password from Isolated Storage and show those in TextBox andCheckBox controls.
protectedoverridevoid OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e){// load and show saved email from isolated storageif(appSettings.Contains(emailKey)){
email =(string)appSettings[emailKey];}
EmailTextBox.Text= email;// load password from isolated storageif(appSettings.Contains(passwordKey)){
password =(string)appSettings[passwordKey];}// username from isolated storageif(appSettings.Contains(usernameKey)){
username =(string)appSettings[usernameKey];}
UsernameTextBox.Text= username;// show password if selected to saveif(appSettings.Contains(savepasswordKey)){string savepass =(string)appSettings[savepasswordKey];if(savepass =="true"){
savepassword =true;
PasswordTextBox.Password= password;}else{
savepassword =false;
PasswordTextBox.Password="";}
SavePasswordCheckBox.IsChecked= savepassword;}}
Albums Page is loaded when Login Button is pressed here in the Login Page.
OnNavigatedFrom()-method will be called when user is leaving from this page. Here we save user information to Isolated Storage.
protectedoverridevoid OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e){// add email to isolated storage
appSettings.Remove(emailKey);
appSettings.Add(emailKey, email);// add savepassword and password to isolated storage
appSettings.Remove(savepasswordKey);
appSettings.Remove(passwordKey);// add username to isolated storage
appSettings.Remove(usernameKey);
appSettings.Add(usernameKey, username);if(SavePasswordCheckBox.IsChecked==true){
appSettings.Add(savepasswordKey, "true");
appSettings.Add(passwordKey, password);}else{
appSettings.Add(savepasswordKey, "false");
appSettings.Add(passwordKey, password);}}
Albums Page
Albums Page display user's Albums in WrapPanel Control.
Design (AlbumsPage.xaml)
Page grid contains ListBox which ItemsPanel has a WrapPanel Control. This is nice and easy way to get content layout horizontally and vertically as a tile view. Silverlight Toolkit offer also TiltEffect which is enabled in every album. So every time when user clicks album cover it moves down a little. Album cover and title are stored inStackPanel which uses vertical orientation.
OnNavigatedTo()-method will be called when this page is displayed. Here we first load stored email, password and optional username from Isolated Storage and define data feed URL. If optional username is defined in Login Page, then this username is included to data feed URL. After that we will start to get authentication from Google.
protectedoverridevoid OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e){base.OnNavigatedTo(e);// load saved username from isolated storageif(appSettings.Contains(usernameKey)){string differentUsername =(string)appSettings[usernameKey];if(differentUsername !="") username = differentUsername;}// load saved email from isolated storageif(appSettings.Contains(emailKey)){
email =(string)appSettings[emailKey];// for example firstname.lastname@gmail.comif(username ==""&& email.IndexOf("@")!=-1) username = email.Substring(0, email.IndexOf("@"));// firstname.lastname
dataFeed =String.Format("http://picasaweb.google.com/data/feed/api/user/{0}?alt=json", username);}// load password from isolated storageif(appSettings.Contains(passwordKey)){
password =(string)appSettings[passwordKey];}// we are coming back from AlbumPageif(e.NavigationMode==System.Windows.Navigation.NavigationMode.Back){
AlbumsListBox.ItemsSource= app.albums;
AlbumsListBox.SelectedIndex=-1;}else{// get authentication from Google
GetAuth();}}
To get authentication to use Picasa services, we have to use WebClient class to download authentication information from Google. With Picasa services, we have to use "lh2" as a service and accountType is "GOOGLE". You can find more information about Google services with Picasa here.
privatevoid GetAuth(){string service ="lh2";// Picasastring accountType ="GOOGLE";
WebClient webClient =new WebClient();
Uri uri =new Uri(string.Format("https://www.google.com/accounts/ClientLogin?Email={0}&Passwd={1}&service={2}&accountType={3}", email, password, service, accountType));
webClient.DownloadStringCompleted+=new DownloadStringCompletedEventHandler(AuthDownloaded);
webClient.DownloadStringAsync(uri);}
Google sends authentication code if login was successful. We can parse it from result and use it to load albums data from Google. Note: Authentication code is stored toApp.xaml.cs file (and album and images data). We will discuss more about this later in this chapter (handling Tombstone mode).
privatevoid AuthDownloaded(object sender, DownloadStringCompletedEventArgs e){try{if(e.Result!=null&& e.Error==null){
app.auth="";int index = e.Result.IndexOf("Auth=");if(index !=-1){
app.auth= e.Result.Substring(index +5);}if(app.auth!=""){// get albums from Google
GetAlbums();return;}}
MessageBox.Show("Cannot get authentication from google. Check your login and password.");}catch(WebException){
MessageBox.Show("Cannot get authentication from google. Check your login and password.");}}
WebClient class is used again to fetch albums data from Google. Note that we have to add authentication to our headers in WebClient object.
privatevoid GetAlbums(){
WebClient webClient =new WebClient();
webClient.Headers[HttpRequestHeader.Authorization]="GoogleLogin auth="+ app.auth;
Uri uri =new Uri(dataFeed, UriKind.Absolute);
webClient.DownloadStringCompleted+=new DownloadStringCompletedEventHandler(AlbumsDownloaded);
webClient.DownloadStringAsync(uri);}
There can be a lot of data returned (depends how much albums you have) from the Google. In this article all the data is stored to App.xaml.cs class to handle Tombstone mode in different views. We have covered Tombstone mode for example here in Movies in Finnkino Theatres article.
Now we only need to know that these variables are handled in App.xaml.cs and they are used in different Pages in this application:
public AlbumList albums =new AlbumList();public AlbumImages albumImages =new AlbumImages();publicint selectedImageIndex;publicint selectedAlbumIndex;publicstring auth;
Back to Albums data. If there are no errors, we will parse JSON string to our collections (AlbumList). First we have to deserialize JSON string to dynamic object, so we can start to find data from JSON string. If you look carefully what kind of JSON string Google returns, you can find for example following object: feed, author and album entries with many different details.
Above WebClient class object will call AlbumsDownloaded()-method when data is returned from Google.
publicvoid AlbumsDownloaded(object sender, DownloadStringCompletedEventArgs e){try{if(e.Result==null|| e.Error!=null){
MessageBox.Show("Cannot get albums data from Picasa server!");return;}else{// Deserialize JSON string to dynamic object
IDictionary<string, object> json =(IDictionary<string, object>)SimpleJson.DeserializeObject(e.Result);// Feed object
IDictionary<string, object> feed =(IDictionary<string, object>)json["feed"];// Author List
IList author =(IList)feed["author"];// First author (and only)
IDictionary<string, object> firstAuthor =(IDictionary<string, object>)author[0];// Album entries
IList entries =(IList)feed["entry"];// Delete previous albums from albums list
app.albums.Clear();// Find album detailsfor(int i =0; i < entries.Count; i++){// Create a new Album
Album album =new Album();// Album entry object
IDictionary<string, object> entry =(IDictionary<string, object>)entries[i];// Published object
IDictionary<string, object> published =(IDictionary<string, object>)entry["published"];// Get published date
album.published=(string)published["$t"];// Title object
IDictionary<string, object> title =(IDictionary<string, object>)entry["title"];// Album title
album.title=(string)title["$t"];// Link List
IList link =(IList)entry["link"];// First link is album data link object
IDictionary<string, object> href =(IDictionary<string, object>)link[0];// Get album data addres
album.href=(string)href["href"];// Media group object
IDictionary<string, object> mediagroup =(IDictionary<string, object>)entry["media$group"];// Media thumbnail object list
IList mediathumbnailList =(IList)mediagroup["media$thumbnail"];// First thumbnail object (smallest)var mediathumbnail =(IDictionary<string, object>)mediathumbnailList[0];// Get thumbnail url
album.thumbnail=(string)mediathumbnail["url"];// Add album to albums
app.albums.Add(album);}// Add albums to AlbumListBox
AlbumsListBox.ItemsSource= app.albums;}}catch(WebException){
MessageBox.Show("Cannot get albums data from Picasa server!");}catch(KeyNotFoundException){
MessageBox.Show("Cannot load images from Picasa Server - JSON parsing error happened!");}}
JSON string is deserialized to IDictionary object. This way we can search data from JSON string like a name value pairs. Above code has a lot of comments to help understand data parsing.
Now all the albums data from Picasa should be visible in Albums Page. Selected album images will be loaded and showed when user select album from AlbumListBox.
privatevoid AlbumsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e){// If real selection is happened, go to a AlbumPageif(AlbumsListBox.SelectedIndex==-1)return;
app.selectedAlbumIndex= AlbumsListBox.SelectedIndex;this.NavigationService.Navigate(new Uri("/AlbumPage.xaml?SelectedIndex="+ AlbumsListBox.SelectedIndex, UriKind.Relative));}
Album Page
Selected album's thumbnail images will be visible in Album Page.
Design (AlbumPage.xaml)
Here we will use the same kind of technique which was used in the previous Albums Page. Grid contains ListBox which uses WrapPanel and finally one item in WrapPanel is only a Image Control. In this article we will use one of the smallest thumbnail image which is available from Google (72x54px). There can be a lot of thumbnails loading, and we are using here PhonePerformance assembly to help loading images (remember add reference to PhonePerformance.dll in your project).
OnNavigatedTo()-method will be called when this page is displayed. Here we first check that is user coming back from the Image Page (we don't have to load album images data again) or is user coming from Main Page (we have to start loading images data from Google).
protectedoverridevoid OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e){base.OnNavigatedTo(e);// We are coming back from Images Pageif(e.NavigationMode==System.Windows.Navigation.NavigationMode.Back){
AlbumImagesListBox.ItemsSource= app.albumImages;
PageTitle.Text= app.albums[app.selectedAlbumIndex].title;
AlbumImagesListBox.SelectedIndex=-1;return;}// We are coming from MainPage, start loading album images
IDictionary<string, string> parameters =this.NavigationContext.QueryString;if(parameters.ContainsKey("SelectedIndex")){int selectedIndex = Int32.Parse(parameters["SelectedIndex"]);
PageTitle.Text= app.albums[selectedIndex].title;
GetImages(selectedIndex);}}
If we are coming from Main Page, then we have to load images data from Google. We have to use WebClient and remember to use Authentication in headers.
ImagesDownloaded()-method will be called when data is loaded from Google. If there are no errors, we will parse JSON data to our collections (AlbumImages). First we have to deserialize JSON String to object to get access to images data. JSON data contains for example Feed and Image entries. Data will be parsed a same way as we did in prevous page (now we are handing images data).
publicvoid ImagesDownloaded(object sender, DownloadStringCompletedEventArgs e){try{if(e.Result==null|| e.Error!=null){
MessageBox.Show("Cannot load images from Picasa server!");return;}else{// Deserialize JSON string to dynamic object
IDictionary<string, object> json =(IDictionary<string, object>)SimpleJson.DeserializeObject(e.Result);// Feed object
IDictionary<string, object> feed =(IDictionary<string, object>)json["feed"];// Number of photos object
IDictionary<string, object> numberOfPhotos =(IDictionary<string, object>)feed["gphoto$numphotos"];// Entries Listvar entries =(IList)feed["entry"];// clear previous images from albumImages
app.albumImages.Clear();// Find image details from entriesfor(int i =0; i < entries.Count; i++){// Create a new albumImage
AlbumImage albumImage =new AlbumImage();// Image entry object
IDictionary<string, object> entry =(IDictionary<string, object>)entries[i];// Image title object
IDictionary<string, object> title =(IDictionary<string, object>)entry["title"];// Get album title
albumImage.title=(string)title["$t"];// Album content object
IDictionary<string, object> content =(IDictionary<string, object>)entry["content"];// Get image src url
albumImage.content=(string)content["src"];// Image width object
IDictionary<string, object> width =(IDictionary<string, object>)entry["gphoto$width"];// Get image width
albumImage.width=(string)width["$t"];// Image height object
IDictionary<string, object> height =(IDictionary<string, object>)entry["gphoto$height"];// Get image height
albumImage.height=(string)height["$t"];// Image size object
IDictionary<string, object> size =(IDictionary<string, object>)entry["gphoto$size"];// Get image size
albumImage.size=(string)size["$t"];// Image media group List
IDictionary<string, object> mediaGroup =(IDictionary<string, object>)entry["media$group"];
IList mediaThumbnailList =(IList)mediaGroup["media$thumbnail"];// First thumbnail object
IDictionary<string, object> mediathumbnail =(IDictionary<string, object>)mediaThumbnailList[0];// Get thumnail url
albumImage.thumbnail=(string)mediathumbnail["url"];// Add albumImage to albumImages Collection
app.albumImages.Add(albumImage);}// Add albumImages to AlbumImagesListBox
AlbumImagesListBox.ItemsSource= app.albumImages;}}catch(WebException){
MessageBox.Show("Cannot load images from Picasa server!");}catch(KeyNotFoundException){
MessageBox.Show("Cannot load images from Picasa Server - JSON parsing error happened!");}}
Now all the selected album images should be visible in Album Page. Selected image will be loaded and showed when the user select image from AlbumImageListBox.
privatevoid AlbumImagesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e){// If real selection is happened, go to a ImagesPageif(AlbumImagesListBox.SelectedIndex==-1)return;this.NavigationService.Navigate(new Uri("/ImagesPage.xaml?SelectedIndex="+ AlbumImagesListBox.SelectedIndex+"&SelectedAlbum="+ PageTitle.Text, UriKind.Relative));}
Images Page
Images Page shows selected album image. This page supports portrait and landscape orientations. User can swipe screen to show previous or next image in this album.
Design (ImagesPage.xaml)
When you create a new page you can select Portrait Page as a template. Later you can modify your page to support both landscape and portrait modes in PagePhoneApplicationPage root element:
OnNavigatedTo()-method will be called when this page is shown. Selected album and selected images are send here from the previous page. First we will find those information and then load image from Google. (LoadImage-method will be covered later in this article).
protectedoverridevoid OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e){base.OnNavigatedTo(e);// Find selected image index from parameters
IDictionary<string, string> parameters =this.NavigationContext.QueryString;if(parameters.ContainsKey("SelectedIndex")){
app.selectedImageIndex= Int32.Parse(parameters["SelectedIndex"]);}else{
app.selectedImageIndex=0;}// Find selected album nameif(parameters.ContainsKey("SelectedAlbum")){
selectedAlbumTitle = parameters["SelectedAlbum"];}else{
selectedAlbumTitle ="No album name";}// Load image from Google
LoadImage();}
Silverlight Toolkit gives Gestures to Silverlight applications. Here we first initialize GestureListener in ImagesPage()-constructor and when drag is completed we will handle the event and display previous or next image in this album.
// Constructorpublic ImagesPage(){
InitializeComponent();// Initialize GestureListener
gestureListener = GestureService.GetGestureListener(ContentPanel);// Handle Dragging (to show next or previous image from Album)
gestureListener.DragCompleted+=new EventHandler<DragCompletedGestureEventArgs>(gestureListener_DragCompleted);}
gestureListener_DragCompleted()-method will be called when drag is completed in screen. If horizontal change is more than zero, we have to show previous image - otherwise we load next image.
// Gesture - Drag is completevoid gestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e){// Left or Rightif(e.HorizontalChange>0){// previous image (or last if first is shown)
app.selectedImageIndex--;if(app.selectedImageIndex<0) app.selectedImageIndex= app.albumImages.Count-1;}else{// next image (or first if last is shown)
app.selectedImageIndex++;if(app.selectedImageIndex>(app.albumImages.Count-1)) app.selectedImageIndex=0;}// Load image from Google
LoadImage();}
Above method will call LoadImage()-method to start loading image from Google Picasa service. All the image URL's are stored in albumImages collection in App.xaml.csclass. We are listening image loading and when image is loaded, we will remove Loading.... -text from the screen.
// Load Image from Googleprivatevoid LoadImage(){// Load a new image
bitmapImage =new BitmapImage(new Uri(app.albumImages[app.selectedImageIndex].content, UriKind.RelativeOrAbsolute));// Handle loading (hide Loading... animation)
bitmapImage.DownloadProgress+=new EventHandler<DownloadProgressEventArgs>(bitmapImage_DownloadProgress);// Loaded Image is image source in XAML
image.Source= bitmapImage;}
// Image is loaded from Googlevoid bitmapImage_DownloadProgress(object sender, DownloadProgressEventArgs e){// Hide loading... animation
ShowProgress =false;// Disable LoadingListener for this image
bitmapImage.DownloadProgress-=new EventHandler<DownloadProgressEventArgs>(bitmapImage_DownloadProgress);// Show image details in UI
ImageInfoTextBlock.Text=String.Format("Album {0} : Image {1} of {2}.",
selectedAlbumTitle,
(app.selectedImageIndex+1),
app.albumImages.Count);}
Summary
This code example shows how you can authenticate to Google services and how you can use different extensions in Windows Phone application. Hope you find this article useful and it helps you work with JSON in WP. on You can download source codes here: File:PTMMyPicasa.zip
Note: While compiling the project for WP8, you may encountered the error 'Could not load the assembly file: Microsoft.Phone.Controls.Toolkit.dll', to overcome this, simply go to project's folder path->MyPicasa->Bin->Debug, right click the specified dll(Microsoft.Phone.Controls.Toolkit.dll), go to its properties and click the Unblock button. That's it, just rebuild the solution and run the code.
Nhận xét
Đăng nhận xét