Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Coding4Fun - Photos Near You

In this article, I'll demonstrate how to use the new Windows 7 location feature to download photos taken in your vicinity from Flickr and use them to update your desktop wallpaper. The utility is written as an addin for my previous Utility Runner project (the MEF application).

Wallpaper changers are a dime-a-dozen, but I haven't seen any that are location-aware.  The Windows 7 location feature makes it easy for applications to discover the system location and allows you to personalize weather, sports scores, news headlines or general search.

This project requires Visual C#/Visual Basic Edition 2008 or higher.  It runs as an addin for Utility Runner, so you'll need to install that before debugging.

Blast From the Past

If you've looked at the MEF-based Utility Runner before, you know it's a light-weight container for running system utilities.  The point is to unclutter your desktop notification area (by the clock) and reduce the overhead of lots of little utilities.  Just like desktop gadgets, you don't need separate processes running.  Unlike desktop gadgets, though, utilities tend to be headless-they don't have a user interface.  Other than a settings form, they just do their thing in the background.

There are only a few extension points. You need to provide metadata about your plugin (e.g. author, URL, description, etc.); methods to initialize, start, and stop; and a user control containing the configuration UI that will be hosted in the main window.  MEF attributes mark up the code and enable the discovery and loading at runtime.

MEF Utility Runner with GeoWallpaper

Since you're developing a plugin, debugging needs to take place within the host.  Once you've installed Utility Runner, you can set its executable file as the process to debug.  When you launch it, it will check for plugins from the current directory and run your code.  This makes it easy to debug using F5.  Of course, creating unit tests to verify your code outside of the host would be a great idea too.

What's Going on Here

This application performs two functions: downloading photos from Flickr based on location and displaying them at intervals as your desktop background.

Upon startup, the application retrieves the current location.  Using this location, a set of nearby photos is obtained.  These photos are downloaded to swap out on the desktop as needed.  If your system location changes, an event is raised.  The event handler in the application performs a fresh Flickr lookup and uses this new collection of images.

The idea of changing desktop background is pretty simple.  You need a BMP or JPG image and a sizing parameter (stretch, center, tile, fit, fill).  This information gets stored in the Windows registry (HKCU/Control Panel/Desktop) and then a Win32 call, SystemParametersInfo, triggers the actual change.  All of this logic is contained in the Wallpaper.vb/cs file. 

Unfortunately there's no way to trigger the new Windows 7 slideshow feature, which would perform the cycling automatically.  You could do it by creating a theme file and pointing it to an RSS feed or image folder, but that's not the most elegant solution.  Users shouldn't need a specific theme to use my application.  It's an extension point, though, if it seems like a good idea to someone else.

 

Finding yourself

The Windows 7 Location API is part of the Sensors API.  This gives you access to current location in a civic format (address, city, state, etc.) or as a latitude/longitude pair.  The location provider on your system determines the accuracy of your reading.

Windows Location

The Sensors and Location API download includes a sample called the Enhanced Default location Provider, which allows you to set your location by clicking on a map.  Another option is to use Geosense for Windows.  This application uses your IP address and/or wireless access point association to determine your location using Google Location Services.  Since Geosense is free, I highly recommend downloading it-but it doesn't help with testing unless you're willing to travel a lot!

The code for obtaining location is very simple:

Visual C#

loc = new LatLongLocationProvider(10000);
loc.LocationChanged += new LocationChangedEventHandler(loc_LocationChanged);
lastLocation = (LatLongLocationReport)(loc.GetReport());

Visual Basic

loc = New LatLongLocationProvider(10000)
AddHandler loc.LocationChanged, New LocationChangedEventHandler(AddressOf loc_LocationChanged)
lastLocation = loc.GetReport()

Four Billion Photos and Counting

Everyone knows that Flickr is the place to go to find photos.  Facebook's popularity (15+ billion photos) is changing this, but if you want pure photo search with fewer party snapshots, Flickr is the place to go.  Thankfully, Yahoo! has a full-featured API available for photos, users and groups.  One of the search features can accept a latitude/longitude pair to find photos geocoded in the vicinity.  Not all photos are geotagged, but many are.

Flickr API's are easy to use, and you can do most work with nothing more than REST calls. To simplify things, though, I decided to use the FlickrNet API hosted on Codeplex, which wraps the API into a nice object-oriented package.  From there you can authorize a user and perform image searches.  User authorization is optional, but this allows us to get any private photos of a user or his/her contacts.  If you know that you uploaded geocoded photos, you might be annoyed when they don't appear in a search.

Authorizing a user is signing him or her in without ever seeing a password.  If you've used other Flickr-based applications, you've seen it work: we request an authentication key from Flickr called a frob (no, I don't know where the name came from), which doesn't mean anything at this point. To authorize the user we need to open a URL (with the frob as a parameter) for Flickr authentication.  The user signs in and that frob is now associated with him or her.  When the user tells us the process is complete, we can query Flickr for an authorization key.  We can save the key for future use so the user only needs to login once.  This key is also associated with the permissions that a user has granted our application.

 

Flickr Authorization

To make calls against Flickr, you need a developer key.  This allows you to authorize users without seeing their password. This is great for users, but not so great for developers.  As long as you're writing closed-source or server-side code, you can hide the key. In an open source application the key is out in the open.  I need to remove this in the included application source, so you'll need to get your own and embed it in the application settings.  Sorry about the inconvenience.

Project settings

Once you're authorized to make calls, actually performing the search is quite straightforward (shown here without exception handling):

Visual C#

PhotoSearchOptions searchOptions = new PhotoSearchOptions();
searchOptions.Tags = FlickrTags;
searchOptions.TagMode = TagMode.AnyTag;
searchOptions.Latitude = (float)location.Latitude;
searchOptions.Longitude = (float)location.Longitude;
searchOptions.Extras = PhotoSearchExtras.OwnerName;

photos = F.PhotosSearch(searchOptions).PhotoCollection;

Properties.Settings.Default.FlickrTags = FlickrTags;
Properties.Settings.Default.Save();
photoIdx = rnd.Next(0, photos.Length - 1);
lastLocation = location;

Visual Basic

Dim searchOptions As New PhotoSearchOptions()
searchOptions.Tags = FlickrTags
searchOptions.TagMode = TagMode.AnyTag
searchOptions.Latitude = location.Latitude
searchOptions.Longitude = location.Longitude
searchOptions.Extras = PhotoSearchExtras.OwnerName

photos = F.PhotosSearch(searchOptions).PhotoCollection

My.Settings.FlickrTags = FlickrTags
My.Settings.Save()
photoIdx = rnd.Next(0, photos.Length - 1)
lastLocation = location

 

You fill out the search tags, the latitude and longitude, and request that the owner's name comes back with the data.  You end up with a collection of objects representing the Flickr objects.  From there, you request the sizes in which the photo is available and download the largest.  The application saves to the system temp folder.  It might be a better idea to create a folder in the My Photos folder, but this way there is a better chance of them being cleaned up over time.

Visual C#

string fn = Path.Combine(Path.GetTempPath(), photo.PhotoId + "_" + photo.Secret + ".jpg");

// If we've already cached it don't download it again
if (!File.Exists(fn))
{
    Sizes s = F.PhotosGetSizes(photo.PhotoId);

    // Use the highest quality available
    string url = s.SizeCollection[s.SizeCollection.Length - 1].Source;

    Stream ds = F.DownloadPicture(url);
    Stream fs = new FileStream(fn, FileMode.CreateNew);
    ds.CopyTo(fs);
    fs.Close();
    ds.Close();
}
                    
Status = "Now showing '" + photo.Title + "' by " + photo.OwnerName;

LastPhoto = photo;
                    
Wallpaper.SetWallpaper(fn, WallpaperStyle.Fit);

 

Visual Basic

Dim fn As String = Path.Combine(Path.GetTempPath(), photo.PhotoId + "_" + photo.Secret & ".jpg")

' If we've already cached it don't download it again
If Not File.Exists(fn) Then
    Dim s As Sizes = F.PhotosGetSizes(photo.PhotoId)

    ' Use the highest quality available
    Dim url As String = s.SizeCollection(s.SizeCollection.Length - 1).Source

    Dim ds As Stream = F.DownloadPicture(url)
    Dim fs As IO.Stream = New FileStream(fn, FileMode.CreateNew)
    ds.CopyTo(fs)
    fs.Close()
    ds.Close()
End If

Status = ("Now showing '" + photo.Title & "' by ") + photo.OwnerName

LastPhoto = photo

Wallpaper.SetWallpaper(fn, WallpaperStyle.Fit)

The CopyTo method is an extension method in the StreamExtensions.vb/cs file.  It simply copies the data from one stream to another - in this case from the download stream to a file stream.

Next Steps

The browser-based Flickr authorization would fit together a little more seamlessly if you embedded a WebBrowser control directly into the UI.  Then you could display the authorization page right along with the rest of the application and the Complete Authorization and Cancel buttons.

You could also do a better job of downloading the closest fit image size to the current display resolution, or display different photos on on a multiple monitor setup.  I've written an article about how to create backgrounds for multiple displays; feel free to use it as a starting point.

Conclusion

As people move from desktops to laptops, location awareness for laptops will become more and more important. Stay ahead of the curve by making your application location-aware.

About Arian

Arian Kulp is a software developer living in Western Oregon.  He creates samples, screencasts, demos, labs, and articles; speaks at programming events; and enjoys spending time with his family.

Tags:

Follow the Discussion

Remove this comment

Remove this thread

close

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.