Error reporting (via email)

fix Hammock rest error handling
This commit is contained in:
2011-03-29 02:17:13 +03:00
parent 00f97e41d6
commit 7de756e3f0
42 changed files with 363 additions and 55 deletions
+5 -1
View File
@@ -8,7 +8,9 @@
mc:Ignorable="d"
xmlns:vm="clr-namespace:MyFriendsAround.WP7.ViewModel"
xmlns:my="clr-namespace:Coding4Fun.Phone.Controls.Converters;assembly=Coding4Fun.Phone.Controls"
xmlns:my1="clr-namespace:Microsoft.Silverlight.Testing.Client;assembly=Microsoft.Silverlight.Testing">
xmlns:my1="clr-namespace:Microsoft.Silverlight.Testing.Client;assembly=Microsoft.Silverlight.Testing"
xmlns:Controls="clr-namespace:Coding4Fun.Phone.Controls;assembly=Coding4Fun.Phone.Controls"
xmlns:Views="clr-namespace:MyFriendsAround.WP7.Views">
<!--Application Resources-->
<Application.Resources>
@@ -16,6 +18,8 @@
d:IsDataSource="True" />
<my:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter1" />
<my1:InvertValueConverter x:Key="InvertValueConverter1" />
</Application.Resources>
<Application.ApplicationLifetimeObjects>
+23 -1
View File
@@ -15,6 +15,7 @@ using Microsoft.Phone.Shell;
using MyFriendsAround.WP7.ViewModel;
using MyFriendsAround.WP7.Utils;
using GalaSoft.MvvmLight.Threading;
using MyFriendsAround.WP7.Views;
namespace MyFriendsAround.WP7
@@ -95,7 +96,7 @@ namespace MyFriendsAround.WP7
}
else
{
Container.Instance.RegisterInstance<AboutViewModel>( new AboutViewModel(), "AboutViewModel");
Container.Instance.RegisterInstance<AboutViewModel>(new AboutViewModel(), "AboutViewModel");
}
}
@@ -109,6 +110,14 @@ namespace MyFriendsAround.WP7
// Code to execute if a navigation fails
void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
//LittleWatson.ReportException(e.Exception, string.Empty);
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var exception = new ExceptionPrompt();
exception.Show(e.Exception);
}
);
if (System.Diagnostics.Debugger.IsAttached)
{
// A navigation has failed; break into the debugger
@@ -119,11 +128,22 @@ namespace MyFriendsAround.WP7
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
//LittleWatson.ReportException(e.ExceptionObject, string.Empty);
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var exception = new ExceptionPrompt();
exception.Show(e.ExceptionObject);
}
);
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
e.Handled = true;
}
@@ -159,6 +179,8 @@ namespace MyFriendsAround.WP7
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
//
//LittleWatson.CheckForPreviousException();
}
#endregion
@@ -20,7 +20,7 @@
</SupportedCultures>
<XapOutputs>true</XapOutputs>
<GenerateSilverlightManifest>true</GenerateSilverlightManifest>
<XapFilename>MyFriendsAround.WP7.xap</XapFilename>
<XapFilename>MyFriendsAround.xap</XapFilename>
<SilverlightManifestTemplate>Properties\AppManifest.xml</SilverlightManifestTemplate>
<SilverlightAppEntry>MyFriendsAround.WP7.App</SilverlightAppEntry>
<ValidateXaml>true</ValidateXaml>
@@ -77,12 +77,18 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>C:\Program Files\Laurent Bugnion (GalaSoft)\Mvvm Light Toolkit\Binaries\WP7\GalaSoft.MvvmLight.Extras.WP7.dll</HintPath>
</Reference>
<Reference Include="Hammock.WindowsPhone">
<HintPath>..\Libs\Hammock-Binaries\.NET 4.0\Windows Phone 7\Hammock.WindowsPhone.dll</HintPath>
<Reference Include="Hammock.WindowsPhone, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\Libs\Hammock\wp7\Hammock.WindowsPhone.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib.Silverlight">
<HintPath>..\packages\Hammock.1.2.1\lib\sl4\ICSharpCode.SharpZipLib.Silverlight.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib.WindowsPhone">
<HintPath>..\Libs\Hammock-Binaries\.NET 4.0\Windows Phone 7\ICSharpCode.SharpZipLib.WindowsPhone.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp">
<HintPath>..\packages\Hammock.1.2.1\lib\sl4\Microsoft.CSharp.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Phone.Controls.Maps, Version=7.0.0.0, Culture=neutral, PublicKeyToken=24eec0d8c86cda1e" />
<Reference Include="Microsoft.Phone.Controls.Toolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b772ad94eb9ca604, processorArchitecture=MSIL">
<HintPath>..\packages\Coding4Fun.Phone.Controls.Complete.1.2\lib\Microsoft.Phone.Controls.Toolkit.dll</HintPath>
@@ -97,7 +103,13 @@
<HintPath>..\Libs\Json40r1\WindowsPhone\Newtonsoft.Json.WindowsPhone.dll</HintPath>
</Reference>
<Reference Include="System.Device" />
<Reference Include="System.Json">
<HintPath>..\packages\Hammock.1.2.1\lib\sl4\System.Json.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Runtime.Serialization.Json">
<HintPath>..\packages\Hammock.1.2.1\lib\sl4\System.Runtime.Serialization.Json.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel" />
<Reference Include="System.Windows.Interactivity">
<SpecificVersion>False</SpecificVersion>
@@ -111,6 +123,10 @@
<Reference Include="System.Core" />
<Reference Include="System.Net" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
<Private>True</Private>
<HintPath>..\packages\Hammock.1.2.1\lib\sl4\System.Xml.Linq.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\MyFriendsAround.Common\Entities\Friend.cs">
@@ -125,9 +141,15 @@
<Compile Include="Helpers\Navigation\IPageNavigation.cs" />
<Compile Include="Helpers\Navigation\PageNavigation.cs" />
<Compile Include="Utils\ApplicationExtensions.cs" />
<Compile Include="Utils\LittleWatson.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Views\AboutPage.xaml.cs">
<DependentUpon>AboutPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ExceptionPrompt.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Views\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
@@ -150,6 +172,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Themes\Generic.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\AboutPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.0">
<App xmlns="" ProductID="{ac5b5d62-573c-4134-b290-0ad4f678ad7f}" Title="MyFriendsAround.WP7" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="MyFriendsAround.WP7 author" Description="Sample description" Publisher="MyFriendsAround.WP7 publisher">
<App xmlns="" ProductID="{ac5b5d62-573c-4134-b290-0ad4f678ad7f}" Title="MyFriendsAround" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="MyFriendsAround.WP7 author" Description="Sample description" Publisher="MyFriendsAround.WP7 publisher">
<IconPath IsRelative="true" IsResource="false">ApplicationIcon.png</IconPath>
<Capabilities>
<Capability Name="ID_CAP_NETWORKING" />
@@ -13,11 +12,11 @@
<Capability Name="ID_CAP_PHONEDIALER" />
<Capability Name="ID_CAP_PUSH_NOTIFICATION" />
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
<Capability Name="ID_CAP_IDENTITY_DEVICE"/>
<Capability Name="ID_CAP_IDENTITY_DEVICE" />
<Capability Name="ID_CAP_IDENTITY_USER" />
</Capabilities>
<Tasks>
<DefaultTask Name ="_default" NavigationPage="Views\MainPage.xaml"/>
<DefaultTask Name="_default" NavigationPage="Views\MainPage.xaml" />
</Tasks>
<Tokens>
<PrimaryToken TokenID="MyFriendsAround.WP7Token" TaskName="_default">
@@ -16,5 +16,6 @@ namespace MyFriendsAround.WP7.Service
public class FriendsListEventArgs: EventArgs
{
public List<Friend> Friends { get; set; }
public Exception Error { get; set; }
}
}
@@ -8,5 +8,6 @@ namespace MyFriendsAround.WP7.Service
public class PublishLocationEventArgs: EventArgs
{
public bool IsSuccess { get; set; }
public Exception Error { get; set; }
}
}
@@ -1,27 +1,17 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Hammock;
using Hammock.Streaming;
using Hammock.Tasks;
using Hammock.Web;
using MyFriendsAround.Common.Entities;
using Hammock.Caching;
using CacheMode = Hammock.Caching.CacheMode;
namespace MyFriendsAround.WP7.Service
{
public static class ServiceAgent
{
private static int _timeOut = 10;
#region GetFriends
private static EventHandler<FriendsListEventArgs> friendscallback;
@@ -32,15 +22,25 @@ namespace MyFriendsAround.WP7.Service
RestClient client = new RestClient
{
Authority = "http://localhost.:55672/myfriends",
Timeout = new TimeSpan(0, 0, 0, _timeOut),
Serializer = serializer,
Deserializer = serializer
};
RestRequest request = new RestRequest
{
Path = "GetFriends" + "?timestamp=" + DateTime.Now.Ticks.ToString()
Path = "GetFriends" + "?timestamp=" + DateTime.Now.Ticks.ToString(),
Timeout = new TimeSpan(0, 0, 0, _timeOut)
};
friendscallback = callback;
client.BeginRequest(request, new RestCallback<List<Friend>>(GetFriendsCallback));
try
{
client.BeginRequest(request, new RestCallback<List<Friend>>(GetFriendsCallback));
}
catch (Exception ex)
{
friendscallback.Invoke(null, new FriendsListEventArgs() { Friends = null, Error = ex });
}
}
public static void GetFriendsCallback(RestRequest request, RestResponse<List<Friend>> response, object userState)
@@ -50,6 +50,10 @@ namespace MyFriendsAround.WP7.Service
List<Friend> list = response.ContentEntity;
friendscallback.Invoke(null, new FriendsListEventArgs() { Friends = list });
}
else
{
friendscallback.Invoke(null, new FriendsListEventArgs() { Friends = null, Error = new Exception("Communication Error!") });
}
}
#endregion
@@ -66,17 +70,27 @@ namespace MyFriendsAround.WP7.Service
RestClient client = new RestClient
{
Authority = "http://localhost.:55672/myfriends",
Timeout = new TimeSpan(0, 0, 0, _timeOut),
Serializer = serializer,
Deserializer = serializer
};
RestRequest request = new RestRequest
{
Timeout = new TimeSpan(0, 0, 0, _timeOut),
Method = WebMethod.Post,
Path = "PublishLocation",
Entity = friend
};
publishlocationcallback = callback;
client.BeginRequest(request, new RestCallback<bool>(PublishLocationCallback));
try
{
client.BeginRequest(request, new RestCallback<bool>(PublishLocationCallback));
}
catch (Exception ex)
{
publishlocationcallback.Invoke(null, new PublishLocationEventArgs() { IsSuccess = false, Error = ex });
}
}
public static void PublishLocationCallback(RestRequest request, RestResponse<bool> response, object userState)
@@ -86,6 +100,10 @@ namespace MyFriendsAround.WP7.Service
bool success = response.ContentEntity;
publishlocationcallback.Invoke(null, new PublishLocationEventArgs() { IsSuccess = success });
}
else
{
publishlocationcallback.Invoke(null, new PublishLocationEventArgs() { IsSuccess = false, Error = new Exception("Communication Error!")});
}
}
#endregion
@@ -0,0 +1,59 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:Coding4Fun.Phone.Controls;assembly=Coding4Fun.Phone.Controls"
xmlns:Views="clr-namespace:MyFriendsAround.WP7.Views">
<Style TargetType="Views:ExceptionPrompt">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Views:ExceptionPrompt">
<Grid>
<Rectangle Fill="Transparent" />
<Border VerticalAlignment="Center"
Width="460"
Height="390"
Background="{StaticResource PhoneChromeBrush}"
BorderThickness="1"
BorderBrush="{StaticResource PhoneForegroundBrush}">
</Border>
<Grid Width="450"
Height="380"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="The program has encountered a problem and needs to close. We are sorry for the inconvenience."
TextWrapping="Wrap"
HorizontalAlignment="Center"
Margin="5"
Style="{StaticResource PhoneTextNormalStyle}" />
<TextBlock Grid.Row="1"
Text="We have created an error report that you can send to the developers."
TextWrapping="Wrap"
HorizontalAlignment="Center"
Margin="5,10,5,5"
Style="{StaticResource PhoneTextNormalStyle}" />
<CheckBox Content="Email report to developer"
Grid.Row="2"
x:Name="canSubmitCheckBox" />
<Controls:RoundButton x:Name="okButton"
VerticalAlignment="Bottom"
HorizontalAlignment="Center"
Grid.Row="3" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,82 @@
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using Microsoft.Phone.Tasks;
namespace MyFriendsAround.WP7.Utils
{
public class LittleWatson
{
const string filename = "LittleWatson.txt";
internal static void ReportException(Exception ex, string extra)
{
try
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
SafeDeleteFile(store);
using (TextWriter output = new StreamWriter(store.CreateFile(filename)))
{
output.WriteLine(extra);
output.WriteLine(ex.Message);
output.WriteLine(ex.StackTrace);
}
}
}
catch (Exception)
{
}
}
internal static void CheckForPreviousException()
{
try
{
string contents = null;
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(filename))
{
using (TextReader reader = new StreamReader(store.OpenFile(filename, FileMode.Open, FileAccess.Read, FileShare.None)))
{
contents = reader.ReadToEnd();
}
SafeDeleteFile(store);
}
}
if (contents != null)
{
if (MessageBox.Show("A problem occurred the last time you ran this application. Would you like to send an email to report it?", "Problem Report", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
{
EmailComposeTask email = new EmailComposeTask();
email.To = "someone@example.com";
email.Subject = "YourAppName auto-generated problem report";
email.Body = contents;
SafeDeleteFile(IsolatedStorageFile.GetUserStoreForApplication()); // line added 1/15/2011
email.Show();
}
}
}
catch (Exception)
{
}
finally
{
SafeDeleteFile(IsolatedStorageFile.GetUserStoreForApplication());
}
}
private static void SafeDeleteFile(IsolatedStorageFile store)
{
try
{
store.DeleteFile(filename);
}
catch (Exception ex)
{
}
}
}
}
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Device.Location;
using System.Net;
using System.Security;
using System.ServiceModel.Channels;
using System.Windows;
using System.Windows.Input;
@@ -17,6 +18,7 @@ using Microsoft.Silverlight.Testing;
using MyFriendsAround.Common.Entities;
using MyFriendsAround.WP7.Service;
using MyFriendsAround.WP7.Utils;
using MyFriendsAround.WP7.Views;
using Newtonsoft.Json;
namespace MyFriendsAround.WP7.ViewModel
@@ -59,7 +61,6 @@ namespace MyFriendsAround.WP7.ViewModel
//
PublishLocationCommand = new RelayCommand(() => PublishLocationAction());
DisplayAboutCommand = new RelayCommand(() => DisplayAbout());
InputBoxCommand = new RelayCommand(() => InputBox());
NavigateToAboutCommand = new RelayCommand(() => NavigateToAbout());
RefreshFriendsCommand = new RelayCommand(() => RefreshFriends());
@@ -80,17 +81,12 @@ namespace MyFriendsAround.WP7.ViewModel
ServiceAgent.GetFriends(this.GetFriendsResult);
}
private void NavigateToAbout()
private void NavigateToAbout()
{
//
this.PageNav.NavigateTo(new Uri("/Views/AboutPage.xaml", UriKind.Relative));
}
private void InputBox()
{
MessageBox.Show("Input box");
}
private void DisplayAbout()
{
@@ -109,7 +105,7 @@ namespace MyFriendsAround.WP7.ViewModel
PinSource = "/ApplicationIcon.png",
Location = new GeoCoordinate(f.Latitude, f.Longitude)
});
});
});
PushPins = result;
}
@@ -127,30 +123,60 @@ namespace MyFriendsAround.WP7.ViewModel
public void GetFriendsResult(object sender, FriendsListEventArgs args)
{
List<Friend> list = args.Friends;
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
PopulatePushPins(list);
IsBusy = false;
}
);
if (args.Error == null)
{
List<Friend> list = args.Friends;
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
PopulatePushPins(list);
IsBusy = false;
}
);
}
else
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
IsBusy = false;
var exception = new ExceptionPrompt();
exception.Show(args.Error);
}
);
}
}
public void PublishLocationResult(object sender, PublishLocationEventArgs args)
{
if (!args.IsSuccess)
if (args.Error != null)
{
var message = new DialogMessage("Communication error!", DialogMessageCallback)
{
Button = MessageBoxButton.OK,
Caption = "Error!"
};
Messenger.Default.Send(message);
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
IsBusy = false;
var exception = new ExceptionPrompt();
exception.Show(args.Error);
});
}
//
//update
ServiceAgent.GetFriends(this.GetFriendsResult);
else
{
if (!args.IsSuccess)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var message = new DialogMessage(
"Communication error!", DialogMessageCallback)
{
Button = MessageBoxButton.OK,
Caption = "Error!"
};
Messenger.Default.Send(message);
});
}
//
//update
ServiceAgent.GetFriends(this.GetFriendsResult);
}
}
private void DialogMessageCallback(MessageBoxResult result)
@@ -167,7 +193,6 @@ namespace MyFriendsAround.WP7.ViewModel
public ICommand PublishLocationCommand { get; set; }
public ICommand DisplayAboutCommand { get; set; }
public ICommand InputBoxCommand { get; set; }
public ICommand NavigateToAboutCommand { get; set; }
public ICommand RefreshFriendsCommand { get; set; }
@@ -198,7 +223,8 @@ namespace MyFriendsAround.WP7.ViewModel
}
public string AppBarTextAbout {
public string AppBarTextAbout
{
get { return "About"; }
}
@@ -260,7 +286,7 @@ namespace MyFriendsAround.WP7.ViewModel
/// The <see cref="MapCenter" /> property's name.
/// </summary>
public const string MapCenterPropertyName = "MapCenter";
private GeoCoordinate _mapCenter = new GeoCoordinate(0,0);
private GeoCoordinate _mapCenter = new GeoCoordinate(0, 0);
/// <summary>
/// Gets the MapCenter property.
@@ -0,0 +1,68 @@
using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using Coding4Fun.Phone.Controls;
namespace MyFriendsAround.WP7.Views
{
public class ExceptionPrompt: PopUp<Exception, PopUpResult>
{
private Button okButton;
private CheckBox submitCheckBox;
private Exception exception;
public ExceptionPrompt()
{
DefaultStyleKey = typeof(ExceptionPrompt);
DataContext = this;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (okButton != null)
okButton.Click -= okButton_Click;
okButton = GetTemplateChild("okButton") as Button;
submitCheckBox = GetTemplateChild("canSubmitCheckBox") as CheckBox;
if (okButton != null)
okButton.Click += okButton_Click;
}
public string To { get; set; }
void okButton_Click(object sender, RoutedEventArgs e)
{
var message = new StringBuilder();
message.Append("Exception type: ");
message.Append(exception.GetType());
message.Append(Environment.NewLine);
message.Append("Message: ");
message.Append(exception.Message);
message.Append(Environment.NewLine);
message.Append("Stack trace: ");
message.Append(exception.StackTrace);
message.ToString();
var task = new Microsoft.Phone.Tasks.EmailComposeTask { Body = message.ToString(), Subject = "Error Report", To = To };
if (submitCheckBox.IsChecked == true)
{
task.Show();
}
OnCompleted(new PopUpEventArgs<Exception, PopUpResult> { PopUpResult = PopUpResult.Ok });
}
public void Show(Exception exception)
{
this.exception = exception;
base.Show();
}
}
}
+2 -2
View File
@@ -38,11 +38,11 @@
</ControlTemplate>
</phone:PhoneApplicationPage.Resources>
<i:Interaction.Triggers>
<!--<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding RefreshFriendsCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</i:Interaction.Triggers>-->
<Grid x:Name="LayoutRoot"
Background="Transparent">
@@ -1,9 +1,11 @@
using System;
using System.Security;
using System.Windows;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Threading;
using Microsoft.Phone.Controls;
using Microsoft.Silverlight.Testing;
using MyFriendsAround.WP7.Views;
namespace MyFriendsAround.WP7
{