Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Life Organizer application

0.00/5 (No votes)
1 Oct 2012 1  
A smart task list that is as portable and compact as the Ultrabook it is designed to run on.

This article is an entry in our AppInnovation Contest. Articles in this sub-section are not required to be full articles so care should be taken when voting.

Introduction

This application is an entry in the App Innovation contest. It is a closed source project so I will not be releasing the full source code. However, I will explain the tricky parts and I will share some of the hurdles I had to overcome. At the end of this article will be more information about where the application is in the development process and where it is going. The Life Organizer application takes full advantage of the advanced features of the Ultrabook, including using the GPS sensor, the touch screen and swipe gestures. The application is essentially built and the major issues have been overcome. The plan is to spend the next month improving the interface, field testing the sensors, and squashing the bugs. The scheduled release to the store is November 1st.

Overview of the Life Organizer

I have developed the Life Organizer after having used and discarded multiple task list applications. Each one was good but lacked some key component. Some worked well for ad hoc tasks. Others tracked goals, but there was no way to relate these goals to my everyday tasks. The Life Organizer is my attempt to bring all of these concepts into one application.

The application has the following features:

  • Easy to use interface
  • Flexible layout for use with landscape, portrait, snapped, and filled views
  • Filtered task list based upon your GPS location
  • Ad-hoc, recurring, and goal-based tasks
  • Smart tasks that can be created when you are at a specific (or unknown) application (an example of this would be a task that reminds you to buy a souvenir for your child when you are on the road)

The Ultrabook was designed for the busy professional who cannot be tied down to just a desk to do work. The Life Organizer application is an extension of that same philosophy. Instead of trying to keep all of your tasks separate, you can store them in the same place and yet keep them separate by physical location. When you open up your Ultrabook at a new location, your tasks for that location will be loaded and ready to go.

The Development Environment

I developed this application as a native Windows 8 Metro application from scratch. My development environment consists of a Windows 8 RC installation running inside of VMWare Fusion on a Macbook Pro. Because I am running the RC version of Windows 8, I had to run the RC version of Visual Studio 2012 even though I have the RTM version. This is because the RTM version will not install on the RC version (it throws a .NET framework error if you try). I also do not have the sensors that I need, which means I had to use the simulator to test out touch, swipe, and the GPS.


The end result of this environment is that I encountered a number of bugs, glitches, and gotchas that slowed down the development process. As I go through the application, I will try to point out these issues as well as what I did to get past them. Hopefully the RTM of Windows 8 and the RTM of Visual Studio 2012 will fix these problems, but I doubt it will get them all.

Developing a Windows 8 Metro Application

The first thing you need to know when you develop a Metro application is that you must build it using Windows 8. There are a number of changes at the Windows level that cannot be replicated in earlier Windows environments. This is an understandable limitation but it is also a difficult one to work with since the RTM of Windows 8 has not yet been released to the general population. You can get it through MSDN and Technet but it is not yet available for sale. You also must be using Visual Studio 2012 for your IDE. When you develop for Windows Metro, you have three options. You can develop in C++, Javascript/HTML5, or XAML and .NET. I decided to develop in XAML and C#.

Layout Changes

Responding to changes in application size and orientation is a key part of a Metro app. There are four major states for an application to plan for. The first state is the standard horizontal full screen view. This is how your application will usually be operated. Here is a screenshot of the Life Organizer in full-screen mode:

There may be times, however, when your application is either started or rotated into Landscape view. The operating system will handle letting your application know that the resolution has changed but you will need to be sure that your application will look good in the changed view. The pattern that Microsoft is pushing is to shrink down the header and the left margin in order to better use the available space. Here is my implementation of the rotated view:



The third and fourth states are called snapped and filled. This is a new feature of Windows that allows one window to be snapped to one side of the screen and the other one fills the rest of the space. The snapped view takes up only 320px. Most applications have a specific look for this view, since you can only show a limited amount of information. For my application, I chose to show just the task list:


When my application moves to the Filled state, not much has to change from the full screen state. In my application, the right panel just adjusts slightly to accommodate the lost space:


All of these different states can happen during the lifespan of your application. In order to make sure that your application takes best advantage of these changes, you will need to monitor the state change event. In my application, I move a couple of pieces around for each view change. Here is the code I use to listen to the size change event:

Window.Current.SizeChanged += MainPage_SizeChanged;

Getting the call to that event was tricky because the way it is referenced has been changed at least three times since the first preview of Windows 8 development. Here is a Stack Overflow question that I found particularly useful in helping me sort this all out: http://stackoverflow.com/questions/10362566/how-to-programmatically-respond-to-snap-in-windows-8-metro

Once you get the event wired up, you need to do something when it is fired. Here is a basic implementation of that event:

private void MainPage_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
    switch (Windows.UI.ViewManagement.ApplicationView.Value)
    {
        case Windows.UI.ViewManagement.ApplicationViewState.Filled:
            widthSpacer.Visibility = Visibility.Collapsed;
            heightSpacer.Visibility = Visibility.Visible;
            break;
        case Windows.UI.ViewManagement.ApplicationViewState.FullScreenLandscape:
            widthSpacer.Visibility = Visibility.Visible;
            heightSpacer.Visibility = Visibility.Visible;
            break;
        case Windows.UI.ViewManagement.ApplicationViewState.FullScreenPortrait:
            widthSpacer.Visibility = Visibility.Collapsed;
            heightSpacer.Visibility = Visibility.Visible;
            break;
        case Windows.UI.ViewManagement.ApplicationViewState.Snapped:
            widthSpacer.Visibility = Visibility.Collapsed;
            heightSpacer.Visibility = Visibility.Collapsed;
            break;
        default:
            break;
    }
}

Note that the above code is just a simple implementation. Based upon the state, spacer elements are either hidden or made visible. This can change how large the margins are on the edges of your application. In my first version of this code, this is how I did a simple adjustment of the interface. My final version will be a bit more complex than this but the basic idea is the same.

Live Tiles and Badges

The Live Tile and the Badge are two other key parts of a Metro app. Not only do they advertise your application, they also allows you an easy way to display information about the data in your application. In my application, I chose to just display how many tasks are currently due by using a badge update. Here is a screenshot of my tile in action:


This code was particularly tricky because you have to load and manipulate XML. Fortunately, I found some great examples on Microsoft's site that helped me get the code right. Here is the final code I use:

private void UpdateBadge(int itemCount)
{
    try
    {
        if (itemCount > 99)
        {
            itemCount = 99;
        }
        if (itemCount < 1)
        {
            //Turns off the badge entirely
            BadgeUpdateManager.CreateBadgeUpdaterForApplication().Clear();
            return;
        }
        var badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
        var badgeElement = (XmlElement)badgeXml.SelectSingleNode("/badge");
        badgeElement.SetAttribute("value", itemCount.ToString());
        var badge = new BadgeNotification(badgeXml);
        BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badge);
    }
    catch (Exception)
    {
        //Turns off the badge when an unexpected error occurs
        BadgeUpdateManager.CreateBadgeUpdaterForApplication().Clear();
    }
}

Note that I am only using the numeric badges (1-99) and not the image updates as well, so I don’t handle those types of data coming in. In my case, I just do two simple checks. First, I check to be sure that the number isn’t higher than 99 (the highest allowable number). If it is, I just put it at the maximum of 99. Next, I check to see if the number is below 1. If it is, I treat it as a reset and I clear the badge entirely from the tile.

GPS Sensor

To enable the use of a GPS sensor, the first thing you need to do is declare that you are going to use the Location capability inside your application manifest file. Here is a shot of where you should be setting this value:


If you do not declare a capability that you then try to use, your application will throw an exception when the code hits the point where the unauthorized capability is called. When you do declare the capability, the user will still need to approve the use of this capability. Here is what that looks like in the running application:


Getting the GPS coordinates isn’t difficult. The one thing you need to be aware of is standard for all parts of Metro applications and that is the need to use the Async/Await keywords. You will need to use threading any time a call could exceed 50ms. This is another standard that Microsoft has laid down for Metro apps to ensure that they stay performant. This means that calls out to devices that might or might not be there (or need to be approved before they can be used) should all use the Async/Await keywords.

In the case of my application, I didn’t just want to capture the GPS coordinates, I wanted to determine if the user was in the same location as a known set of coordinates. I did this by using a distance formula that outputs the end result in kilometers. I then chose to say that anything within 100 meters (0.1 km) was the same location. I did this because your place of business might be large and the accuracy of the GPS is not exact. I did not think that two different locations would be located within 100 meters of each other. This could be adjusted if a need is found. Here is the GPS capture code:

async public void LoadLocation()
{
    try
    {
        // Get cancellation token
        _cts = new CancellationTokenSource();
        CancellationToken token = _cts.Token;


        // Carry out the operation
        Geoposition pos = await _geolocator.GetGeopositionAsync().AsTask(token);


        _latitude = pos.Coordinate.Latitude;
        _longitude = pos.Coordinate.Longitude;
        _accuracy = pos.Coordinate.Accuracy;


        LocationUpdated(this, new EventArgs());
    }
    catch (System.UnauthorizedAccessException)
    {
        _latitude = 0;
        _longitude = 0;
        _accuracy = 0;
    }
    catch (TaskCanceledException)
    {
        _latitude = 0;
        _longitude = 0;
        _accuracy = 0;
    }
    catch (Exception)
    {
        _latitude = 0;
        _longitude = 0;
        _accuracy = 0;
    }
    finally
    {
        _cts = null;
    }
}

Note that I have three catch exception blocks here. I did this for demonstration purposes, since in my case they all do the same thing. You can use these blocks to do different things based upon the circumstances. For instance, if the UnauthorizedAccessException is thrown, you could let the user know that they cannot receive the benefits of having a GPS sensor if they don't allow it to be used (or something similarly snarky).

You may also note from the above code that I am raising a LocationUpdated event. This is a custom event that I created for this implementation. I listen to this event and when it gets hit, I read the new coordinates information out and act upon that data.

When I was dealing with the GPS data, I created a couple of extension methods to help make my life easier, as well as a struct. The struct simply holds the latitude and longitude of a position. This makes it easier to pass coordinate data around. Here is that struct:

public struct Position
{
    public double Latitude;
    public double Longitude;
}

The two extension methods I created were specific to identifying the distance between two points. The first method, called ToRadians converts a double to its equivalent in radians. This is an important part of the location formula. Next is the actual GetDistance method. This method is an extension method for the Position struct and it takes in one more position value. It uses a common formula for location distance (not something I came up with on my own). Here are these two extension methods:

public static double GetDistance(this Position orig, Position dest)
{
    double R = 6378.1370D;
    double dLat = dest.Latitude - orig.Latitude;
    double dLon = dest.Longitude - orig.Longitude;


    dLat = dLat.ToRadians();
    dLon = dLon.ToRadians();


    double a = Math.Sin(dLat / 2) *
        Math.Sin(dLat / 2) +
        Math.Cos(orig.Latitude.ToRadians()) *
        Math.Cos(dest.Latitude.ToRadians()) *
        Math.Sin(dLon / 2) *
        Math.Sin(dLon / 2);


    double c = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)));
    double d = R * c;
    return d;
}


public static double ToRadians(this double value)
{
    return (Math.PI / 180) * value;
}

Wrapping Up

So that is the basics of how to build a Metro application and some of the specifics of how I built my application. As I mentioned at the beginning of the article, my intent is to have the first version of this application live in the app store on November 1st. When the application goes live, I will have all of the features listed above. However, I’m not going to be satisfied stopping there. I’m already beginning work on a list of additional features that should make the first major update to the application. These include:

  • Azure database support (currently this app uses just SQLite) for cloud-based storage of data.
  • Theme switching based upon ambient lighting (light versus dark theme).
  • Goal reporting - a reporting screen that shows the progress on a particular goal.
  • Achievements - badges that encourage you in your progress on your tasks. These will show up on an achievements page and they will appear as toast notifications when they occur.

I also have a couple of ideas that I cannot begin working on until I get the Ultrabook hardware. These features will be in the second major update of the Life Organizer application. They include:

  • Windows Phone 8 app with support for syncing between the Ultrabook and the phone both through the cloud and through the wireless network.
  • Near Field Communication (NFC) between Ultrabooks or between an Ultrabook and a phone to give tasks to someone else (or to receive them).

Conclusion

I hope that you have seen the value of my application and I hope you have also learned something about how to develop a Windows 8 Metro application. Please let me know your thoughts below. I always appreciate constructive feedback. For those of you who are wondering about the look of my application, I am planning on working with a designer friend of mine before I launch the application. Hopefully we can work together to raise the bar on the visuals of the app.

History

  • October 1, 2012 - Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here