Quick overview of tools

Currently the project consists of the following tools:

StateVariables

The StateVariables class let's you easily add and retrieve items from the state cache.
It uses the PhoneApplicationService.Current.State Dictionary to store your variables. You can only store values that are serializable! The values are lost when you close the application. Use PersitentVariables to persist your variables.

VersionManagement

The VersionManagement class let's you retrieve your current application version and enables you to check the current application version against a txt file on a remote server. It uses the Version attribute from the WMAppManifest.xml which is always updated to the latest version number when publishing your app on the marketplace.

The checking of the application version against a txt file on a remote server enables you to inform the user, on startup of the app, that a newer version of your application is available.

LittleWatson

The LittleWatson class let's you catch unhandled exception and automatically report them back to you through a webservice or let the user e-mail to you the next time the user opens up the application.
This implementation of LittleWatson is an extension of the originally implemented LittleWatson by Andy Pennell:
http://blogs.msdn.com/b/andypennell/archive/2010/11/01/error-reporting-on-windows-phone-7.aspx

Notification Control

The Notification control is a great way to inform the user about an event. The notification control will popup in the top of the screen and looks like a Toast Notification. You can attach a click event to the notification so you can redirect the user to a different page if clicked.

PersitentVariables

PersitentVariables use the IsolatedStorage to store Variables. The Variables will still be there when you close the program and re-open it.

NagForReview

NagForReview will ask, after a fix set of days and/or application starts, the user to review your app. NagForReview is based on the NotificationBox from Tomer Shamam, but i cleaned up the code a bit and fitted to my requirements.

InputBox

The InputBox enables you to show a popup with an input field. InputBox is based on the NotificationBox from Tomer Shamam.

Symmetric Encryption

Two simple methods to encrypt and decrypt data on your phone. Using AES encryption with a 256bit keysize and PBKDF2 using HMACSHA1.

ProtectData

An open-source version of ProtectedData. Microsoft already includes a ProtectedData in the Windows Phone SDK but the internals of the ProtectedData methods are not available and can therefore not be trusted. Especially in light of all the latest news where Apple and Google are handing over passwords and tools to decipher phones of users. The NorthernLight ProtectData implementation uses proven encryption methods and is completely open-source and can be confirmed against other systems which use the same implementation of the encryption method. The perfect way to protect the data of your user. It uses the symmetric encryption method and persists your data on the device. Using AES encryption with a 256bit keysize and PBKDF2 using HMACSHA1.

Asymmetric Encryption

Asymmetrically encrypt messages using RSA AES encryption. This implementation is a simple wrapper around the scrypt RSA cryptography library.

Code Examples


StateVariables

Store a value:

NorthernLights.StateVariables.Set<string>("MyPersonalVariable", "MyValue");

Retrieve a value:

string value = NorthernLights.StateVariables.Get<string>("MyPersonalVariable");

VersionManagement

Get current Version as Version object

Version version = NorthernLights.VersionManagement.GetVersion();

Check current App version against a appversion.txt file on remote server:

NorthernLights.VersionManagement.CheckForNewVersion("http://www.yourwebsite.com/appversion.txt", () =>
{
  // new version available. open the marketplace.
});

NOTE: Caching can prevent the VersionManagement from working correctly. The CacheControl and Pragma headers on the HttpWebRequest don't seem to work. As a solution you can add a ever changing parameter to the Uri by doing the following

string uri = string.format("{0}?{1}", "http://www.yourwebsite.com/appversion.txt", DateTime.Now.Ticks);

appversion.txt
1.0.0.0


LittleWatson

First of all we need to setup the application to catch unhandled exceptions and save them for LittleWatson to report

In App.xaml.cs, in the Public Constructor, setup an UnhandledException handler:
        /// <summary>
        /// Constructor for the Application object.
        /// </summary>
        public App()
        {
            // Global handler for uncaught exceptions. 
            this.UnhandledException += Application_UnhandledException;
        }

Then add the Application_UnhandledException code in the App.xaml.cs file

        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            LittleWatson.SaveExceptionForReporting(e.ExceptionObject as Exception);

            if (System.Diagnostics.Debugger.IsAttached)
            {
                // An unhandled exception has occurred; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

Now we need to add code in your Main application page to check LittleWatson for an Unhandled Exception that might need to be reported. Depending on the boolean 'AllowAnonymousHttpReporting' the exception will automatically be reported through an HTTP endpoint or the user will have to intervene and send the exception report by e-mail.

            ExceptionContainer exception = LittleWatson.GetPreviousException();

            if (exception != null)
            {
                if (LittleWatson.Instance.AllowAnonymousHttpReporting)
                {
                    LittleWatson.Instance.SendExceptionToHttpEndpoint("http://www.yourdomain.com/data/post.php", exception);
                }
                else
                {
                    // show popup.
                    Deployment.Current.Dispatcher.BeginInvoke(() =>
                    {
                                EmailComposeTask email = new EmailComposeTask();
                                email.To = "info@youremailaddress.com";
                                email.Subject = "My App: auto-generated problem report";
                                email.Body = exception.Message + Environment.NewLine + exception.StackTrace;
                                email.Show();
                    });
                }
            }

The code for the post.php file
<?

$result = "";

if (get_magic_quotes_gpc())
{
    $result .= stripslashes($_POST['exception']);
}
else
{
    $result .= $_POST['exception'];
}

$to = "info@youremailaddress.com";
$subject = "Your App: auto-generated problem report";
$headers = "From: info@yoursupportemailaddress.nl\r\n";

mail($to, $subject, $result, $headers);

?>


Notification Control

Show a Notification:

            this.notification.Show(
                "This is a test",
                new SolidColorBrush(Colors.Green),
                () =>
                {
                    /* action to execute on tap */
                });

PersitentVariables

Create a Persitent Variable
            NorthernLights.PersistentVariables.Set<string>("MyPersonalVariable", "MyValue");

Retrieve a Persitent Variable
            string value = NorthernLights.PersistentVariables.Get<string>("MyPersonalVariable");

NagForReview

To use NagForReview you will need to define a style for your NagForReview popup. You can copy the NotificationBoxStyle.xaml from the example which is:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:controls="clr-namespace:NorthernLights.Controls;assembly=NorthernLights.Controls"
                    xmlns:tools="clr-namespace:NorthernLights.Controls.Tools;assembly=NorthernLights.Controls"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
                    mc:Ignorable="d">

    <!--xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Primitives"-->

    <DataTemplate x:Key="NotificationActionDefaultTemplate">
        <TextBlock Text="{Binding Content}" />
    </DataTemplate>

    <Style TargetType="controls:NotificationBoxItem">
        <Setter Property="HorizontalAlignment"
                Value="Stretch" />
        <Setter Property="VerticalAlignment"
                Value="Stretch" />
        <Setter Property="VerticalContentAlignment"
                Value="Center" />
        <Setter Property="HorizontalContentAlignment"
                Value="Center" />
        <Setter Property="Background"
                Value="Transparent" />
        <Setter Property="BorderBrush"
                Value="{StaticResource PhoneForegroundBrush}" />
        <Setter Property="Foreground"
                Value="{StaticResource PhoneForegroundBrush}" />
        <Setter Property="BorderThickness"
                Value="{StaticResource PhoneBorderThickness}" />
        <Setter Property="FontFamily"
                Value="{StaticResource PhoneFontFamilySemiBold}" />
        <Setter Property="FontSize"
                Value="{StaticResource PhoneFontSizeMediumLarge}" />
        <Setter Property="Padding"
                Value="10,3,10,5" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="controls:NotificationBoxItem">
                    <Grid Background="Transparent">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="MouseOver" />
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
                                                                       Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="{StaticResource PhoneBackgroundBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
                                                                       Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="{StaticResource PhoneForegroundBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
                                                                       Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="{StaticResource PhoneForegroundBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
                                                                       Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="{StaticResource PhoneDisabledBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
                                                                       Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="{StaticResource PhoneDisabledBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
                                                                       Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                                                    Value="Transparent" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="ButtonBackground"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Background="{TemplateBinding Background}"
                                CornerRadius="0"
                                Margin="{StaticResource PhoneTouchTargetOverhang}">
                            <ContentControl x:Name="ContentContainer"
                                            ContentTemplate="{TemplateBinding ContentTemplate}"
                                            Content="{TemplateBinding Content}"
                                            Foreground="{TemplateBinding Foreground}"
                                            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                            Padding="{TemplateBinding Padding}"
                                            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="controls:NotificationBox">
        <Setter Property="ItemTemplate"
                Value="{StaticResource NotificationActionDefaultTemplate}" />
        <Setter Property="FontFamily"
                Value="{StaticResource PhoneFontFamilyNormal}" />
        <Setter Property="FontSize"
                Value="{StaticResource PhoneFontSizeNormal}" />
        <Setter Property="Foreground"
                Value="{StaticResource PhoneForegroundBrush}" />
        <Setter Property="Background"
                Value="#99000000" />
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <!--<telerik:RadUniformGrid ChildrenFlow="Horizontal" NumberOfRows="1" />-->
                    <toolkit:WrapPanel Orientation="Horizontal" ItemWidth="Auto" />
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="controls:NotificationBox">
                    <Border Background="#99000000"
                            d:DesignWidth="312"
                            d:DesignHeight="464">
                        <Border x:Name="border"
                                Background="#FF1F1F1F"
                                VerticalAlignment="Top">
                            <Grid Margin="24,40,24,0">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="60" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition />
                                    <RowDefinition Height="Auto" />
                                </Grid.RowDefinitions>

                                <TextBlock Text="{TemplateBinding Title}"
                                           VerticalAlignment="Center"
                                           Margin="0,0,0,2"
                                           FontSize="32" />
                                <TextBlock Text="{TemplateBinding Message}"
                                           TextWrapping="Wrap"
                                           Grid.Row="1"
                                           Margin="0,2,0,2"
                                           FontSize="22"
                                           Foreground="#FFDDDDDD" />

                                <ItemsPresenter Grid.Row="2"
                                                Margin="-12,2,0,8" />

                                <CheckBox IsChecked="{Binding ShowAgainOption, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                          Visibility="{TemplateBinding ShowAgainVisibility}"
                                          Content="{TemplateBinding ShowAgainText}"
                                          Grid.Row="3"
                                          VerticalAlignment="Center"
                                          HorizontalAlignment="Left" />
                            </Grid>
                        </Border>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

If you have the Telerik Controls for Windows Phone I recommend updating the WrapPanel to the RadUniformGrid. This will make sure the buttons will use all the available space which will make it look nicer.

You will need to update this code in the in the Style and add a reference to the telerik namespace.

                    <!--<telerik:RadUniformGrid ChildrenFlow="Horizontal" NumberOfRows="1" />-->
                    <toolkit:WrapPanel Orientation="Horizontal" ItemWidth="Auto" />

Don't forget to add your Style to the App.xaml ResourcesDictionary

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Styles/NotificationBoxStyle.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

Then you can use the NagForReview screen by using the following code in your Main page. This example checks both it the number of days has been reached and if the number of application starts has reach the desired number. a -1 value means the option will be discarded. So this NagForReview will only show up after each two startups of your application.

            NagForReview.Check(Controls.NagForReview.NagType.AfterXDays | Controls.NagForReview.NagType.AfterXTimes, -1, 2);

Example of how the NagForReview screen looks:

nagscreen.png

InputBox

More to come...
            // need code example.

Symmetric Encryption

A simple symmetric encryption which takes the data to encrypt, a password and salt. All in byte arrays. You can use the JSON serializer in the NorthernLights library to encrypt a object by converting it into JSON string and then convert it to a byte array.

You should keep the password secret, but you can share the salt or store it as a persistent variable on your device. You will need to same salt for decrypting the message. The ByteGenerator class, part of NorthernLights, lets you generate a random byte array as input for your password and/or salt.

            byte[] dataToEncrypt = Encoding.UTF8.GetBytes("my secret");
            byte[] password = ByteGenerator.Generate(400);
            byte[] salt = ByteGenerator.Generate(400);

            byte[] encryptedData = Encryption.Symmetric.Encryption.Encrypt(dataToEncrypt, password, salt);
            byte[] mydata = Encryption.Symmetric.Encryption.Decrypt(encryptedData, password, salt);

            string mysecret = Encoding.UTF8.GetString(mydata, 0, mydata.Length);

ProtectData

The Protect generic method takes the object type, passphrase, key and serializable data object as input. The key parameter enables you to store multiple data objects for the same application.

The Unprotect generic method takes the object type, passphrase and key of the data object to restore and returns the stored data object as the specified type.

            Encryption.ProtectData.Protect<string>("my incredibily complex long passphrase.", "myappdata", "my secret");
            string mysecret = Encryption.ProtectData.Unprotect<string>("my incredibily complex long passphrase.", "myappdata");

Asymmetric Encryption

More to come...

Last edited Apr 10, 2012 at 3:45 AM by southernsun, version 44

Comments

No comments yet.