Monday, December 20, 2010

Listen to your Phone - Getting User Feedback with System Information

Two weeks ago I've blogged about a trick my Windows Phone Pictures Lab app uses to detect if the Zune software is running. Today I have a handful of classes that help to gather system information and provide an easy interface for the email task. No magic at all, but I though it might be helpful for others too.
I use these helpers in Pictures Lab for the user feedback functionality. I got a lot of valuable feedback and great suggestions via this feature.
The user can invoke this functionality on the about page of the app and it's also fired when an unhandled exception occurred and the user wants to send error details.

System Information
It's always useful to include some system, version and device information in such a feedback report. That's why I wrote a SytemInformation class which queries some paramteres and creates a nice, human readable string.
public class SystemInformation
{
   public static string Dump()
   {
      var builder = new StringBuilder();
      builder.AppendLine("***** System Infos *****");
      builder.AppendLine();
      builder.AppendFormat("Time: {0}", DateTime.Now.ToUniversalTime().ToString("r"));
      builder.AppendLine();
      builder.AppendFormat("Culture: {0}", CultureInfo.CurrentCulture);
      builder.AppendLine(); 
      builder.AppendFormat("App Version: {0}", WmAppManifestHelper.GetVersion());
      builder.AppendLine();  
      builder.AppendFormat("Manufacturer: {0}", DevicePropertiesHelper.GetDeviceManufacturer());
      builder.AppendLine(); 
      builder.AppendFormat("Model: {0}", DevicePropertiesHelper.GetDeviceModel());
      builder.AppendLine(); 
      builder.AppendFormat("DeviceHardwareVersion: {0}", DevicePropertiesHelper.GeDeviceHardwareVersion());
      builder.AppendLine(); 
      builder.AppendFormat("DeviceFirmwareVersion: {0}", DevicePropertiesHelper.GeDeviceFirmwareVersion());
      builder.AppendLine(); 
      builder.AppendFormat("OS  Version: {0}", Environment.OSVersion);
      builder.AppendLine(); 
      builder.AppendFormat("CLR Version: {0}", Environment.Version);
      builder.AppendLine(); 
      builder.AppendFormat("Device Type: {0}", Microsoft.Devices.Environment.DeviceType);
      builder.AppendLine(); 
      builder.AppendFormat("Network Type: {0}", NetworkInterface.NetworkInterfaceType);
      builder.AppendLine(); 
      builder.AppendFormat("DeviceTotalMemory: {0:f3} MB", DevicePropertiesHelper.GetTotalMemoryMb());
      builder.AppendLine(); 
      builder.AppendFormat("ApplicationPeakMemoryUsage: {0:f3} MB", DevicePropertiesHelper.GetPeakMemoryUsageMb());
      builder.AppendLine(); 
      builder.AppendFormat("ApplicationCurrentMemoryUsage: {0:f3} MB", DevicePropertiesHelper.GetCurrentMemoryUsageMb());
      builder.AppendLine(); 
      using (var appStorage = IsolatedStorageFile.GetUserStoreForApplication())
      {
         builder.AppendFormat("Iso Storage AvailableFreeSpace: {0:f3} MB", appStorage.AvailableFreeSpace / 1024f / 1024f);
         builder.AppendLine();
      }
      return builder.ToString();
   }
}
As you can see a few interesting properties from the OS, the device and its configuration and memory status are queried. This is done with built-in and custom helper classes.
The WmAppManifestHelper class parses the WmAppManifest.xml file and provides the app version and other properties from this file. The DevicePropertiesHelper wraps the DeviceExtendedProperties class and the available property keys. I added all the keys I need, but not more (YAGNI). However, if you add more property keys to the class, please make sure to post a comment.


Email Service
The EmailService class wraps the EmailComposeTask and adds the system infos to the body of the email.
public class EmailService
{
   private readonly EmailComposeTask emailComposeTask;

   public string Addressee { get; set; }

   public EmailService()
   {      
      Addressee = "foo@bar.com";
      emailComposeTask = new EmailComposeTask();
   }

   public void ShowTask(string subject, string body)
   {
      // Add system infos add the end of the email
      body += Environment.NewLine;
      body += Environment.NewLine;
      body += Environment.NewLine;
      body += Environment.NewLine;
      body += SystemInformation.Dump();

      // Fill email
      emailComposeTask.To = Addressee;
      emailComposeTask.Subject = subject;
      emailComposeTask.Body = body;

      // Invoke Mail Task
      emailComposeTask.Show();
   }
}
The ShowTask method sets the properties of the EmailCompoaseTask and opens it. The addressee can be defined with the corresponding property.


Please replace the foo@bar.com default value for Addressee with your email address. Think about the poor Foo at bar.com.










Handling the Unhandled
This email functionality is used to enable explicit user feedback when the user taps the 'submit feedback' button. It is also utilized in the UnhandledException event handler of the Application class. The default body of the event handler in the App.xaml.cs file is generated by Visual Studio when a new Windows Phone Application project is created. Just add the email service and set the e.Handled property to true.
private static void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
   var result = MessageBox.Show("An error occured. Do you want to send the error details to the developer?", "Meh", MessageBoxButton.OKCancel);
   if (result == MessageBoxResult.OK)
   {
      new EmailService().ShowTask("App error", e.ExceptionObject.ToString());
   } 
   e.Handled = true;
}

The body of such an error email would then look like this
System.ArgumentException: The parameter is incorrect. 
   at MS.Internal.XcpImports.CheckHResult(UInt32 hr)
   at MS.Internal.XcpImports.SetValue(INativeCoreTypeWrapper obj, DependencyProperty property, DependencyObject doh)
   at MS.Internal.XcpImports.SetValue(INativeCoreTypeWrapper doh, DependencyProperty property, Object obj)
...


***** System Infos *****

Time: 12/11/2010 5:50:02 PM
Culture: en-US
App Version: 1.9.0.0
Manufacturer: SAMSUNG
Modell: SGH-i917
DeviceHardwareVersion: 3.1.0.7
DeviceFirmwareVersion: 2103.10.10.1
OS  Version: Microsoft Windows CE 7.0.7004
CLR Version: 3.7.10218.0
Device Type: Device
Network Type: MobileBroadbandGsm
DeviceTotalMemory: 475.469 MB
ApplicationPeakMemoryUsage: 63.055 MB
ApplicationCurrentMemoryUsage: 41.223 MB
Iso Storage AvailableFreeSpace: 601.641 MB

Conclusion
User feedback is always valuable and every serious Windows Phone developer should at least add a simple functionality like the one presented here.

Source Code
You can download all mentioned classes here. Please comment if you know more useful system infos that should be included.

2 comments:

  1. very nice, just like something i had in mind.

    my idea was to have a data collector that will gather information (like the one you showed), but perhaps other app specific data.

    also in my implementation, there's some interface used to actually "send" the data after collected, not necessarily an email.

    this can allow for periodic data collection & reporting to some collecting web-service, for example.

    ReplyDelete