Capture usage information of a Windows Phone 7 application using Google Analytics

About half a year ago I created a Blend behavior to capture usage information of a Silverlight application. But what’s next, in the near future we will have Windows Phone 7, and we can write very nice applications by using the Silverlight platform.

Although the platform is the same, you won’t be able to make use of the Blend behavior I’ve written before. Because this Blend behavior makes use of scripting inside the hosting html page. There simply is no page that hosts your Silverlight application. This is a similar problem we face when a Silverlight application is running out-of-browser, but back to the phone.

Solution Direction

To connect to Google Analytics I need to have a html page that contains a script that calls into Google Analytics to track events. So somehow I need to connect a Windows Phone 7 app to this html page.

Solution Implementation

I started off with the html page, and made it as small as possible, no content, only the scripting part.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Windows Phone 7 - Application Tracking</title>
    <script type="text/javascript">
        var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
        document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
	</script>
    <script type="text/javascript">
        var pageTracker = _gat._getTracker("UA-xxxxx-x");
        pageTracker._trackPageview();

        function trackEvent(category, action, label) {
            pageTracker._trackEvent(category, action, label);
        }
    </script>
</head>
<body>
</body>
</html>

I hosted it somewhere so it’s accessible through the internet. If you copy-paste this code please make sure that you fill in your own Google Analytics tracking code.

I’ve seen some code examples where Windows Phone 7 apps were hosting a webbrowser and where the app was communicating to the page hosted in the app. So that’s where I began as well.

Add a WebBrowser control to the MainPage.xaml, I named it trackingBrowser

<browser:WebBrowser x:Name="trackingBrowser"/>

Make sure that scripts are enabled on the WebBrowser control

image

In the end it’s not required to make it visible at all so we can set the Visibility to Collapsed. If you’re not comfortable with not showing the browser during development you can of course leave the browser visible.

image

The resulting xaml will look similar to this.

<browser:WebBrowser x:Name="trackingBrowser" 
                    IsScriptEnabled="True" 
                    Visibility="Collapsed" />

The WebBrowser needs to load the create html page. So in the Loaded event of the containing page I set the url of the WebBrowser.

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    trackingBrowser.Navigate(new Uri("http://change-to-your-domain.com/tracking.htm"));
}

As soon as the WebBrowser navigated to this url I want to execute the trackEvent inside the html page. To call Javascript on a WebBrowser control you will have to make use of the InvokeScript method. Because the Navigated event doesn’t come back to the UI Thread we will need to make use of the Dispatcher to actually call the trackEvent method.

trackingBrowser.Navigated += (s, args) =>
                                Dispatcher.BeginInvoke(
                                    () => trackingBrowser.InvokeScript("trackEvent",
                                                                       "WindowsPhoneExperiment",
                                                                       "Load",
                                                                       "On Loading of Application"));

For the sake of the experiment I also added a button to the form to track events on button click. The event handler for the button-click looks like this.

private void button1_Click(object sender, RoutedEventArgs e)
{
    trackingBrowser.InvokeScript("trackEvent",
                                 "WindowsPhoneExperiment",
                                 "Click",
                                 "On Click of a Button");
}

That’s all, and now we wait…

Result

It’s very important to view the result. It will take some time before the Google Analytics stats are working. I waited a day before I looked at the stats. Good to see the concept works.

image

Full code

MainPage.xaml

<phoneNavigation:PhoneApplicationPage x:Class="MM.Phone.Experiments.MainPage"
                                      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                                      xmlns:phoneNavigation="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Navigation"
                                      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                                      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                                      mc:Ignorable="d"
                                      d:DesignWidth="480"
                                      d:DesignHeight="800"
                                      FontFamily="{StaticResource PhoneFontFamilyNormal}"
                                      FontSize="{StaticResource PhoneFontSizeNormal}"
                                      Foreground="{StaticResource PhoneForegroundBrush}"
                                      xmlns:browser="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.WebBrowser"
                                      Loaded="PhoneApplicationPage_Loaded">

    <Grid x:Name="LayoutRoot"
          Background="{StaticResource PhoneBackgroundBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <!--TitleGrid is the name of the application and page title-->
        <Grid x:Name="TitleGrid"
              Grid.Row="0">
            <TextBlock Text="MM Phone Experiments"
                       x:Name="textBlockPageTitle"
                       Style="{StaticResource PhoneTextPageTitle1Style}" />
            <TextBlock Text="Analytics"
                       x:Name="textBlockListTitle"
                       Style="{StaticResource PhoneTextPageTitle2Style}" />
            <Button Content="Button"
                    Height="70"
                    HorizontalAlignment="Left"
                    Margin="320,77,0,0"
                    Name="button1"
                    VerticalAlignment="Top"
                    Width="160"
                    Click="button1_Click" />
        </Grid>

        <!--ContentGrid is empty. Place new content here-->
        <Grid x:Name="ContentGrid"
              Grid.Row="1">
            <browser:WebBrowser x:Name="trackingBrowser"
                                IsScriptEnabled="True"
                                Visibility="Collapsed" />
        </Grid>
    </Grid>

</phoneNavigation:PhoneApplicationPage>

MainPage.xaml.cs

using System;
using System.Windows;
using Microsoft.Phone.Controls;

namespace MM.Phone.Experiments
{
    public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();

            SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

            trackingBrowser.Navigated += (s, args) =>
                                         Dispatcher.BeginInvoke(
                                             () => trackingBrowser.InvokeScript("trackEvent",
                                                                                "WindowsPhoneExperiment",
                                                                                "Load",
                                                                                "On Loading of Application"));
        }

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {
            trackingBrowser.Navigate(new Uri("http://change-to-your-domain.com/tracking.htm"));
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            trackingBrowser.InvokeScript("trackEvent",
                                         "WindowsPhoneExperiment",
                                         "Click",
                                         "On Click of a Button");
        }
    }
}

tracking.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Windows Phone 7 - Application Tracking</title>
    <script type="text/javascript">
        var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
        document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
	</script>
    <script type="text/javascript">
        var pageTracker = _gat._getTracker("UA-xxxxx-x");
        pageTracker._trackPageview();

        function trackEvent(category, action, label) {
            pageTracker._trackEvent(category, action, label);
        }
    </script>
</head>
<body>
</body>
</html>

How to improve the Windows Phone 7 Licensing development experience?

Microsoft really helps developers to implement licensing for your application. First of all everyone has probably read: “an app can only be installed through the Marketplace”. But beside this installation, an application can be tried through the Marketplace. The developer doesn’t have to write complex licensing mechanisms based on RPN strings. If you want to have limited features when the app is in Trial, it’s just one small piece of code. First reference the correct library.

Add Reference to Microsoft.Phone.License

The the code is really simple.

LicenseInfo licenseInfo = new LicenseInfo();
if(licenseInfo.IsTrial())
{
    //Limit the application in some way
}

Yes that’s all. But how does this code behave? As far as I’ve understood so far, in the final version the IsTrial will check the license with the Marketplace. Right now the behavior is just returning “true”, so that doesn’t help our development, we can’t influence this result in any documented way right now. So I was thinking about possible solution, and remember the Conditional Compilation Symbols.

Step 1: Create a New Solution Configuration

New Solution Configuration

Make sure that you use Copy settings from “Debug” and “Create new project configurations”.

Step 2: Change Project Properties

Add a Conditional Compilation Symbol, for example: “FULL_VERSION”

Project Properties with Conditional Compilation Symbol

Step 3: Use this Compilation Symbol

LicenseInfo licenseInfo = new LicenseInfo();
#if FULL_Version
if (false)
{
    //Full Version so this code is not executed.
#else
if(licenseInfo.IsTrial())
{
    //Trial Version
#endif
}

Step 4: Switch between different trial and full version

You can now switch from trial (Debug) to full (Debug Full Version) and back again using the Solution Configuration Selector

Solution Configuration Selector

Hope this helps people who want to develop trial and full versions of their apps.