diff --git a/CompassVO.sln b/CompassVO.sln
new file mode 100644
index 0000000..f3d1303
--- /dev/null
+++ b/CompassVO.sln
@@ -0,0 +1,59 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26127.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompassVO", "CompassVO\CompassVO.csproj", "{AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phone7.Fx.Preview", "Phone7.Fx.Preview\Phone7.Fx.Preview.csproj", "{B55A0F90-2B5A-4C4B-88F4-013AA1629866}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{60E3BC0B-053D-498A-B5F1-8F953EFA4C07}"
+ ProjectSection(SolutionItems) = preProject
+ ReadMe.md = ReadMe.md
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|ARM = Debug|ARM
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|ARM = Release|ARM
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|ARM.ActiveCfg = Debug|ARM
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|ARM.Build.0 = Debug|ARM
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|ARM.Deploy.0 = Debug|ARM
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|x86.ActiveCfg = Debug|x86
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|x86.Build.0 = Debug|x86
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Debug|x86.Deploy.0 = Debug|x86
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|ARM.ActiveCfg = Release|ARM
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|ARM.Build.0 = Release|ARM
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|ARM.Deploy.0 = Release|ARM
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|x86.ActiveCfg = Release|x86
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|x86.Build.0 = Release|x86
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}.Release|x86.Deploy.0 = Release|x86
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Debug|ARM.ActiveCfg = Debug|ARM
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Debug|ARM.Build.0 = Debug|ARM
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Debug|x86.ActiveCfg = Debug|x86
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Debug|x86.Build.0 = Debug|x86
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Release|ARM.ActiveCfg = Release|ARM
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Release|ARM.Build.0 = Release|ARM
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Release|x86.ActiveCfg = Release|x86
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/CompassVO/AboutPage.xaml b/CompassVO/AboutPage.xaml
new file mode 100644
index 0000000..3129cd4
--- /dev/null
+++ b/CompassVO/AboutPage.xaml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Version: 1.2.0.0
+
+
+ ChangeLog:
+ - multiple fixes: (heading declinations, calibration crash)
+ - tweaks on orienteering theme
+ - new themes: digital and night
+ - new feature: taking map photo by hardware camera button
+
+
+ Carefully designed by
+
+
+
+
+
+
+
+
+
+
+ and
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Thank you for your feedback and ideas!
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/AboutPage.xaml.cs b/CompassVO/AboutPage.xaml.cs
new file mode 100644
index 0000000..7bb254d
--- /dev/null
+++ b/CompassVO/AboutPage.xaml.cs
@@ -0,0 +1,12 @@
+using Microsoft.Phone.Controls;
+
+namespace CompassVO
+{
+ public partial class AboutPage : PhoneApplicationPage
+ {
+ public AboutPage()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/App.xaml b/CompassVO/App.xaml
new file mode 100644
index 0000000..b4ce5d8
--- /dev/null
+++ b/CompassVO/App.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+ /Resources/Fonts/LCDMU.TTF#LCDMono
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/App.xaml.cs b/CompassVO/App.xaml.cs
new file mode 100644
index 0000000..6948d1c
--- /dev/null
+++ b/CompassVO/App.xaml.cs
@@ -0,0 +1,196 @@
+using CompassVO.Model;
+using CompassVO.Utils.Logging;
+using CompassVO.ViewModel;
+using GalaSoft.MvvmLight.Threading;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Navigation;
+
+namespace CompassVO
+{
+ public partial class App : Application
+ {
+ public static bool IgnoreAllErrors { get; set; }
+
+ private static bool _canExit = true;
+
+ public static bool CanExit
+ {
+ get { return _canExit; }
+ set { _canExit = value; }
+ }
+
+ ///
+ /// Provides easy access to the root frame of the Phone Application.
+ ///
+ /// The root frame of the Phone Application.
+ public PhoneApplicationFrame RootFrame { get; private set; }
+
+ ///
+ /// Constructor for the Application object.
+ ///
+ public App()
+ {
+ // Global handler for uncaught exceptions.
+ UnhandledException += Application_UnhandledException;
+
+ DispatcherHelper.Initialize();
+
+ // Standard Silverlight initialization
+ InitializeComponent();
+
+ // Phone-specific initialization
+ InitializePhoneApplication();
+
+ // Show graphics profiling information while debugging.
+ if (System.Diagnostics.Debugger.IsAttached)
+ {
+ // Display the current frame rate counters.
+ Application.Current.Host.Settings.EnableFrameRateCounter = true;
+
+ // Show the areas of the app that are being redrawn in each frame.
+ //Application.Current.Host.Settings.EnableRedrawRegions = true;
+
+ // Enable non-production analysis visualization mode,
+ // which shows areas of a page that are handed off to GPU with a colored overlay.
+ //Application.Current.Host.Settings.EnableCacheVisualization = true;
+
+ // Disable the application idle detection by setting the UserIdleDetectionMode property of the
+ // application's PhoneApplicationService object to Disabled.
+ // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
+ // and consume battery power when the user is not using the phone.
+ PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
+ }
+ }
+
+ // Code to execute when the application is launching (eg, from Start)
+ // This code will not execute when the application is reactivated
+ private void Application_Launching(object sender, LaunchingEventArgs e)
+ {
+ }
+
+ // Code to execute when the application is activated (brought to foreground)
+ // This code will not execute when the application is first launched
+ private void Application_Activated(object sender, ActivatedEventArgs e)
+ {
+ }
+
+ // Code to execute when the application is deactivated (sent to background)
+ // This code will not execute when the application is closing
+ private void Application_Deactivated(object sender, DeactivatedEventArgs e)
+ {
+ }
+
+ // Code to execute when the application is closing (eg, user hit Back)
+ // This code will not execute when the application is deactivated
+ private void Application_Closing(object sender, ClosingEventArgs e)
+ {
+ if (Locator != null)
+ Locator.Cleanup();
+ }
+
+ // Code to execute if a navigation fails
+ private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
+ {
+ if (System.Diagnostics.Debugger.IsAttached)
+ {
+ // A navigation has failed; break into the debugger
+ System.Diagnostics.Debugger.Break();
+ }
+ }
+
+ // Code to execute on Unhandled Exceptions
+ private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
+ {
+ #region Error Logging
+
+ if (IgnoreAllErrors)
+ {
+ e.Handled = true;
+ return;
+ }
+
+ if (e.ExceptionObject is QuitException)
+ {
+ return;
+ }
+ if (e.ExceptionObject is WebException)
+ {
+ MessageBox.Show(e.ExceptionObject.Message);
+ }
+ else
+ {
+ ShowException(e.ExceptionObject);
+ }
+
+ #endregion Error Logging
+
+ if (System.Diagnostics.Debugger.IsAttached)
+ {
+ // An unhandled exception has occurred; break into the debugger
+ System.Diagnostics.Debugger.Break();
+ }
+ else
+ {
+ e.Handled = true;
+ }
+ }
+
+ private void ShowException(Exception ex)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(() =>
+ {
+ var exception = new ExceptionPrompt();
+ exception.To = Constants.JWP_SUPPORT_EMAIL;
+ exception.Show(ex);
+ });
+ }
+
+ #region Phone application initialization
+
+ // Avoid double-initialization
+ private bool phoneApplicationInitialized = false;
+
+ // Do not add any additional code to this method
+ private void InitializePhoneApplication()
+ {
+ if (phoneApplicationInitialized)
+ return;
+
+ // Create the frame but don't set it as RootVisual yet; this allows the splash
+ // screen to remain active until the application is ready to render.
+ RootFrame = new PhoneApplicationFrame();
+ RootFrame.Navigated += CompleteInitializePhoneApplication;
+
+ // Handle navigation failures
+ RootFrame.NavigationFailed += RootFrame_NavigationFailed;
+
+ // Ensure we don't initialize again
+ phoneApplicationInitialized = true;
+ }
+
+ // Do not add any additional code to this method
+ private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
+ {
+ // Set the root visual to allow the application to render
+ if (RootVisual != RootFrame)
+ RootVisual = RootFrame;
+
+ // Remove this handler since it is no longer needed
+ RootFrame.Navigated -= CompleteInitializePhoneApplication;
+ }
+
+ #endregion Phone application initialization
+
+ public static ViewModelLocator Locator
+ {
+ get
+ {
+ return App.Current.Resources["Locator"] as ViewModelLocator;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/ApplicationIcon.png b/CompassVO/ApplicationIcon.png
new file mode 100644
index 0000000..d0cf5f0
Binary files /dev/null and b/CompassVO/ApplicationIcon.png differ
diff --git a/CompassVO/Background.png b/CompassVO/Background.png
new file mode 100644
index 0000000..91f13e4
Binary files /dev/null and b/CompassVO/Background.png differ
diff --git a/CompassVO/CompassVO.csproj b/CompassVO/CompassVO.csproj
new file mode 100644
index 0000000..c2f6d04
--- /dev/null
+++ b/CompassVO/CompassVO.csproj
@@ -0,0 +1,366 @@
+
+
+
+ Debug
+ AnyCPU
+ 10.0.20506
+ 2.0
+ {AC16D6A0-B6AA-4732-9AC4-A0C35EF75B97}
+ {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ CompassVO
+ CompassVO
+ v8.0
+
+
+
+
+ WindowsPhone
+ true
+
+
+ true
+ true
+ CompassVO_$(Configuration)_$(Platform).xap
+ Properties\AppManifest.xml
+ CompassVO.App
+ true
+ true
+
+
+
+
+ 4.0
+ 11.0
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+ true
+ full
+ false
+ Bin\Debug
+ DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE
+ true
+ true
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ Bin\Release
+ TRACE;SILVERLIGHT;WINDOWS_PHONE
+ true
+ true
+ prompt
+ 4
+
+
+
+ Bin\x86\Debug
+ true
+ full
+ false
+
+
+
+ Bin\x86\Release
+ pdbonly
+ true
+
+
+
+ Bin\ARM\Debug
+ true
+ full
+ false
+
+
+
+ Bin\ARM\Release
+ pdbonly
+ true
+
+
+
+ ..\packages\Coding4Fun.Phone.Controls.Complete.1.4.6\lib\Coding4Fun.Phone.Controls.dll
+
+
+ ..\packages\Coding4Fun.Phone.Controls.Complete.1.4.6\lib\Coding4Fun.Phone.Controls.Toolkit.dll
+
+
+
+
+ ..\packages\microioc.1.0\lib\MicroIoc.dll
+
+
+
+ False
+ ..\packages\SilverlightToolkitWP.4.2011.8.17\lib\sl4-windowsphone71\Microsoft.Phone.Controls.Toolkit.dll
+
+
+
+
+
+
+
+ AboutPage.xaml
+
+
+ App.xaml
+
+
+
+
+ MainPage.xaml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SettingsPage.xaml
+
+
+
+ compass_metro_graphic_UI.xaml
+
+
+
+ compass_cardinal_UI.xaml
+
+
+
+ compass_night_UI.xaml
+
+
+
+ compass_orienteering_UI.xaml
+
+
+
+ compass_digital_UI.xaml
+
+
+
+
+
+
+
+
+
+
+
+
+ compass_calibration.xaml
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+
+
+
+ Designer
+
+
+ PreserveNewest
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}
+ Phone7.Fx.Preview
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Converters/ConditionalTextConverter.cs b/CompassVO/Converters/ConditionalTextConverter.cs
new file mode 100644
index 0000000..c089b1b
--- /dev/null
+++ b/CompassVO/Converters/ConditionalTextConverter.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace CompassVO.Converters
+{
+ public class ConditionalTextConverter : IValueConverter
+ {
+ #region Implementation of IValueConverter
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value != null && parameter != null && value is bool)
+ {
+ bool boolVal = (bool)value;
+ string[] texts = parameter.ToString().Split('|');
+ if (texts.Length == 2)
+ {
+ return boolVal ? texts[0] : texts[1];
+ }
+ }
+ return null;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return null;
+ }
+
+ #endregion Implementation of IValueConverter
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Converters/NegativeNumberConverter.cs b/CompassVO/Converters/NegativeNumberConverter.cs
new file mode 100644
index 0000000..2422157
--- /dev/null
+++ b/CompassVO/Converters/NegativeNumberConverter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace CompassVO.Converters
+{
+ public class NegativeNumberConverter : IValueConverter
+ {
+ #region Implementation of IValueConverter
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value != null)
+ {
+ if (value is int)
+ {
+ return -(int)value;
+ }
+ if (value is double)
+ {
+ return -(double)value;
+ }
+ if (value is long)
+ {
+ return -(long)value;
+ }
+ if (value is decimal)
+ {
+ return -(decimal)value;
+ }
+ }
+ return null;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return null;
+ }
+
+ #endregion Implementation of IValueConverter
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Images/appbar.check.rest.png b/CompassVO/Images/appbar.check.rest.png
new file mode 100644
index 0000000..7a07466
Binary files /dev/null and b/CompassVO/Images/appbar.check.rest.png differ
diff --git a/CompassVO/Images/appbar.delete.rest.png b/CompassVO/Images/appbar.delete.rest.png
new file mode 100644
index 0000000..95bb16d
Binary files /dev/null and b/CompassVO/Images/appbar.delete.rest.png differ
diff --git a/CompassVO/Images/appbar.feature.camera.rest.png b/CompassVO/Images/appbar.feature.camera.rest.png
new file mode 100644
index 0000000..8c61a4e
Binary files /dev/null and b/CompassVO/Images/appbar.feature.camera.rest.png differ
diff --git a/CompassVO/Images/appbar.feature.settings.rest.png b/CompassVO/Images/appbar.feature.settings.rest.png
new file mode 100644
index 0000000..899b983
Binary files /dev/null and b/CompassVO/Images/appbar.feature.settings.rest.png differ
diff --git a/CompassVO/Images/appbar.folder.rest.png b/CompassVO/Images/appbar.folder.rest.png
new file mode 100644
index 0000000..22d2279
Binary files /dev/null and b/CompassVO/Images/appbar.folder.rest.png differ
diff --git a/CompassVO/Images/banner_logo.png b/CompassVO/Images/banner_logo.png
new file mode 100644
index 0000000..e6cec5d
Binary files /dev/null and b/CompassVO/Images/banner_logo.png differ
diff --git a/CompassVO/Images/banner_logo_100.png b/CompassVO/Images/banner_logo_100.png
new file mode 100644
index 0000000..4f8e8a1
Binary files /dev/null and b/CompassVO/Images/banner_logo_100.png differ
diff --git a/CompassVO/Images/calibrate_compass.png b/CompassVO/Images/calibrate_compass.png
new file mode 100644
index 0000000..6f110b2
Binary files /dev/null and b/CompassVO/Images/calibrate_compass.png differ
diff --git a/CompassVO/Images/icon_background_black.png b/CompassVO/Images/icon_background_black.png
new file mode 100644
index 0000000..8e04519
Binary files /dev/null and b/CompassVO/Images/icon_background_black.png differ
diff --git a/CompassVO/Images/icon_background_white.png b/CompassVO/Images/icon_background_white.png
new file mode 100644
index 0000000..3132560
Binary files /dev/null and b/CompassVO/Images/icon_background_white.png differ
diff --git a/CompassVO/Images/icon_dial_black.png b/CompassVO/Images/icon_dial_black.png
new file mode 100644
index 0000000..ddbc191
Binary files /dev/null and b/CompassVO/Images/icon_dial_black.png differ
diff --git a/CompassVO/Images/icon_dial_lock.png b/CompassVO/Images/icon_dial_lock.png
new file mode 100644
index 0000000..ccad053
Binary files /dev/null and b/CompassVO/Images/icon_dial_lock.png differ
diff --git a/CompassVO/Images/icon_dial_unlock.png b/CompassVO/Images/icon_dial_unlock.png
new file mode 100644
index 0000000..fe42aed
Binary files /dev/null and b/CompassVO/Images/icon_dial_unlock.png differ
diff --git a/CompassVO/Images/icon_dial_white.png b/CompassVO/Images/icon_dial_white.png
new file mode 100644
index 0000000..df15695
Binary files /dev/null and b/CompassVO/Images/icon_dial_white.png differ
diff --git a/CompassVO/Images/icon_map.png b/CompassVO/Images/icon_map.png
new file mode 100644
index 0000000..06aed71
Binary files /dev/null and b/CompassVO/Images/icon_map.png differ
diff --git a/CompassVO/Images/jwp_banner_452.jpg b/CompassVO/Images/jwp_banner_452.jpg
new file mode 100644
index 0000000..f97a30e
Binary files /dev/null and b/CompassVO/Images/jwp_banner_452.jpg differ
diff --git a/CompassVO/Images/pinch_zoom_72.png b/CompassVO/Images/pinch_zoom_72.png
new file mode 100644
index 0000000..84cb55c
Binary files /dev/null and b/CompassVO/Images/pinch_zoom_72.png differ
diff --git a/CompassVO/MainPage.xaml b/CompassVO/MainPage.xaml
new file mode 100644
index 0000000..b14cc51
--- /dev/null
+++ b/CompassVO/MainPage.xaml
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading ...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Starting camera ...
+
+
+
+
+
+ Saving photo ...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/MainPage.xaml.cs b/CompassVO/MainPage.xaml.cs
new file mode 100644
index 0000000..01c33c4
--- /dev/null
+++ b/CompassVO/MainPage.xaml.cs
@@ -0,0 +1,368 @@
+using CompassVO.Model;
+using CompassVO.Model.Messages;
+using CompassVO.Utils;
+using GalaSoft.MvvmLight.Messaging;
+using GalaSoft.MvvmLight.Threading;
+using Microsoft.Devices;
+using Microsoft.Phone.Controls;
+using Microsoft.Xna.Framework.Media;
+using Phone7.Fx.Preview;
+using System;
+using System.IO;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace CompassVO
+{
+ public partial class MainPage : PhoneApplicationPage
+ {
+ private PhotoCamera cam;
+ private UserControl compass_control;
+ private bool isCamInit = false;
+
+ // Constructor
+ public MainPage()
+ {
+ InitializeComponent();
+ Messenger.Default.Register(this, (res) => CreateCompassTheme());
+ Messenger.Default.Register(this, (res) => FillAppBar());
+ Messenger.Default.Register(this, (res) => InitCamera(res));
+ Messenger.Default.Register(this, (res) => TakePhoto());
+ Messenger.Default.Register(this, (res) => LoadPhoto());
+ }
+
+ protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
+ {
+ if (App.Locator.Main.IsBackgroundImageEdit && App.Locator.Main.IsCameraActive && !App.Locator.Main.HasPhoto)
+ {
+ App.Locator.Main.IsInitCamera = true;
+ InitCamera(new ShowCameraPreviewMessage(true));
+ }
+ base.OnNavigatedTo(e);
+ }
+
+ private void LoadPhoto()
+ {
+ if (App.Locator.Main.HasPhoto)
+ {
+ WriteableBitmap bmp = IsolatedStorageHelper.LoadFromLocalStorage(Constants.FINAL_PHOTO_FILENAME, Constants.PHOTO_FOLDER);
+ backPhoto.Source = bmp;
+ backPhoto.UpdateLayout();
+ }
+ }
+
+ private void InitCamera(ShowCameraPreviewMessage res)
+ {
+ if (cam != null)
+ cam.Dispose();
+ cam = null;
+ isCamInit = false;
+
+ if (res.ShowCamera)
+ {
+ if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true))
+ {
+ // Otherwise, use standard camera on back of device.
+ cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);
+ cam.Initialized += new EventHandler(cam_Initialized);
+ cam.CaptureImageAvailable += new EventHandler(cam_CaptureImageAvailable);
+ CameraButtons.ShutterKeyHalfPressed += new EventHandler(CameraButtons_ShutterKeyHalfPressed);
+ CameraButtons.ShutterKeyReleased += new EventHandler(CameraButtons_ShutterKeyReleased);
+ CameraButtons.ShutterKeyPressed += new EventHandler(CameraButtons_ShutterKeyPressed);
+ //Set the VideoBrush source to the camera.
+ viewfinderBrush.SetSource(cam);
+ }
+ }
+ }
+
+ private void CameraButtons_ShutterKeyPressed(object sender, EventArgs e)
+ {
+ if (cam != null)
+ {
+ try
+ {
+ cam.CaptureImage();
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+
+ private void CameraButtons_ShutterKeyReleased(object sender, EventArgs e)
+ {
+ if (cam != null)
+ {
+ cam.CancelFocus();
+ }
+ }
+
+ private void CameraButtons_ShutterKeyHalfPressed(object sender, EventArgs e)
+ {
+ if (cam != null)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(() =>
+ {
+ cam.Focus();
+ });
+ }
+ }
+
+ private void cam_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
+ {
+ Dispatcher.BeginInvoke(() =>
+ {
+ App.Locator.Main.IsCameraActive = false;
+ App.Locator.Main.IsSavingPhoto = true;
+ MediaHelper.PlaySound("Sounds\\camera.wav");
+ InitCamera(new ShowCameraPreviewMessage(false));
+ });
+ using (MemoryStream ms = new MemoryStream())
+ {
+ byte[] buffer = new byte[4096];
+ int len = 0;
+ while ((len = e.ImageStream.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ ms.Write(buffer, 0, len);
+ }
+ buffer = ms.ToArray();
+ IsolatedStorageHelper.SaveToLocalStorage(Constants.TEMP_PHOTO_FILENAME, Constants.PHOTO_FOLDER, buffer);
+ //save also to media lib
+ MediaLibrary myMediaLibrary = new MediaLibrary();
+ myMediaLibrary.SavePicture(string.Format("mapscan_{0}.jpg", Guid.NewGuid().ToString("N")), buffer);
+ }
+ //
+ Dispatcher.BeginInvoke(() =>
+ {
+ WriteableBitmap bmp = IsolatedStorageHelper.LoadFromLocalStorage(Constants.TEMP_PHOTO_FILENAME, Constants.PHOTO_FOLDER);
+ backPhoto.Source = bmp;
+
+ App.Locator.Main.PhotoOffsetX = -(double)bmp.PixelWidth / 2;
+ App.Locator.Main.PhotoOffsetY = -(double)bmp.PixelHeight / 2;
+ App.Locator.Main.PhotoScale = (double)1066 / bmp.PixelWidth;
+ App.Locator.Main.PhotoAngle = 90;
+ App.Locator.Main.SavePhotoSettings();
+
+ App.Locator.Main.HasPhoto = true;
+ FillAppBar();
+ App.Locator.Main.IsSavingPhoto = false;
+ });
+ }
+
+ private void cam_Initialized(object sender, CameraOperationCompletedEventArgs e)
+ {
+ isCamInit = true;
+ Dispatcher.BeginInvoke(() =>
+ {
+ App.Locator.Main.IsInitCamera = false;
+ });
+ }
+
+ private void FillAppBar()
+ {
+ AppBar.Buttons.Clear();
+ if (App.Locator.Main.IsCompassMode)
+ {
+ BindableApplicationBarIconButton button1 = new BindableApplicationBarIconButton();
+ button1.IconUri = new Uri("/Images/icon_map.png", UriKind.Relative);
+ button1.Text = "set map";
+ button1.Command = App.Locator.Main.BackPictureEdit;
+ AppBar.Buttons.Add(button1);
+
+ if (App.Locator.Main.CurrentTheme.SupportsDialRotation)
+ {
+ BindableApplicationBarIconButton button2 = new BindableApplicationBarIconButton();
+ button2.IconUri = new Uri(App.Locator.Main.IsDialEdit ? "/Images/icon_dial_lock.png" : "/Images/icon_dial_unlock.png", UriKind.Relative);
+ button2.Text = "dial";
+ button2.Command = App.Locator.Main.CompassDialEdit;
+ AppBar.Buttons.Add(button2);
+ }
+
+ BindableApplicationBarIconButton button3 = new BindableApplicationBarIconButton();
+ button3.IconUri = new Uri("/Images/appbar.feature.settings.rest.png", UriKind.Relative);
+ button3.Text = "settings";
+ button3.Command = App.Locator.Main.ShowSettings;
+ AppBar.Buttons.Add(button3);
+ }
+ else if (App.Locator.Main.IsBackgroundImageEdit)
+ {
+ if (!App.Locator.Main.HasPhoto)
+ {
+ //no photo
+ BindableApplicationBarIconButton button1 = new BindableApplicationBarIconButton();
+ button1.IconUri = new Uri("/Images/appbar.feature.camera.rest.png", UriKind.Relative);
+ button1.Text = "photo";
+ button1.Command = App.Locator.Main.TakePhotoCommand;
+ AppBar.Buttons.Add(button1);
+
+ BindableApplicationBarIconButton button2 = new BindableApplicationBarIconButton();
+ button2.IconUri = new Uri("/Images/appbar.folder.rest.png", UriKind.Relative);
+ button2.Text = "browse";
+ button2.Command = App.Locator.Main.SelectPhotoCommand;
+ AppBar.Buttons.Add(button2);
+ }
+ else
+ {
+ BindableApplicationBarIconButton button1 = new BindableApplicationBarIconButton();
+ button1.IconUri = new Uri("/Images/appbar.check.rest.png", UriKind.Relative);
+ button1.Text = "accept";
+ button1.Command = App.Locator.Main.AcceptPhotoEdit;
+ AppBar.Buttons.Add(button1);
+ //has photo
+ BindableApplicationBarIconButton button3 = new BindableApplicationBarIconButton();
+ button3.IconUri = new Uri("/Images/appbar.delete.rest.png", UriKind.Relative);
+ button3.Text = "remove";
+ button3.Command = App.Locator.Main.RemovePhotoCommand;
+ AppBar.Buttons.Add(button3);
+ }
+ }
+ else if (App.Locator.Main.IsDialEdit)
+ {
+ BindableApplicationBarIconButton button1 = new BindableApplicationBarIconButton();
+ button1.IconUri = new Uri("/Images/appbar.check.rest.png", UriKind.Relative);
+ button1.Text = "accept";
+ button1.Command = App.Locator.Main.AcceptDialEdit;
+ AppBar.Buttons.Add(button1);
+ }
+
+ //about
+ BindableApplicationBarMenuItem menu1 = new BindableApplicationBarMenuItem();
+ menu1.Text = "about";
+ menu1.Command = App.Locator.Main.ShowAbout;
+ AppBar.MenuItems.Add(menu1);
+ }
+
+ public void CreateCompassTheme()
+ {
+ if (App.Locator.Main.CurrentTheme != null)
+ {
+ compassContainer.Children.Clear();
+ //
+ compass_control = (UserControl)Activator.CreateInstance(App.Locator.Main.CurrentTheme.UIcomponent);
+ compass_control.Width = 480;
+ compass_control.Height = 800;
+ compass_control.HorizontalAlignment = HorizontalAlignment.Center;
+ compass_control.VerticalAlignment = VerticalAlignment.Center;
+ compassContainer.Children.Add(compass_control);
+ compass_control.UpdateLayout();
+
+ //back color
+ LayoutRoot.Background = new SolidColorBrush(App.Locator.Main.CurrentTheme.BackgroundColor);
+ }
+ }
+
+ private void PhoneApplicationPage_Unloaded(object sender, RoutedEventArgs e)
+ {
+ //
+ }
+
+ private void ContentPanel_DoubleTap(object sender, System.Windows.Input.GestureEventArgs e)
+ {
+ if (cam != null && isCamInit && cam.IsFocusSupported)
+ {
+ cam.CancelFocus();
+ cam.Focus();
+ }
+ }
+
+ private void ContentPanel_ManipulationStarted(object sender, System.Windows.Input.ManipulationStartedEventArgs e)
+ {
+ if (App.Locator.Main.IsDialEdit)
+ {
+ MatrixTransform pt = (MatrixTransform)e.ManipulationContainer.TransformToVisual(LayoutRoot);
+ App.Locator.Main.CompassDialEditStarted(new Point(pt.Matrix.OffsetX + e.ManipulationOrigin.X, pt.Matrix.OffsetY + e.ManipulationOrigin.Y));
+ }
+ }
+
+ private void ContentPanel_ManipulationDelta(object sender, System.Windows.Input.ManipulationDeltaEventArgs e)
+ {
+ if (App.Locator.Main.IsDialEdit)
+ {
+ MatrixTransform pt = (MatrixTransform)e.ManipulationContainer.TransformToVisual(LayoutRoot);
+ App.Locator.Main.CompassDialEditDelta(new Point(pt.Matrix.OffsetX + e.ManipulationOrigin.X, pt.Matrix.OffsetY + e.ManipulationOrigin.Y));
+ }
+ }
+
+ private void ContentPanel_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
+ {
+ if (App.Locator.Main.IsDialEdit)
+ {
+ App.Locator.Main.CompassDialEditCompleted();
+ }
+ }
+
+ private void PhoneApplicationPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ //
+ if (!App.Locator.Main.IsCompassMode)
+ {
+ App.Locator.Main.CancelEdit();
+ e.Cancel = true;
+ }
+ }
+
+ public void TakePhoto()
+ {
+ if (cam != null && isCamInit)
+ {
+ try
+ {
+ cam.CaptureImage();
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+
+ private void gl_DragStarted(object sender, DragStartedGestureEventArgs e)
+ {
+ if (App.Locator.Main.IsBackgroundImageEdit && App.Locator.Main.HasPhoto)
+ {
+ //
+ }
+ }
+
+ private const double Rad2Deg = 180.0 / Math.PI;
+ private const double Deg2Rad = Math.PI / 180.0;
+
+ private void gl_DragDelta(object sender, DragDeltaGestureEventArgs e)
+ {
+ if (App.Locator.Main.IsBackgroundImageEdit && App.Locator.Main.HasPhoto)
+ {
+ double radians = -App.Locator.Main.PhotoAngle * Deg2Rad;
+ double msin = Math.Sin(radians);
+ double mcos = Math.Cos(radians);
+ double x = mcos * e.HorizontalChange - msin * e.VerticalChange;
+ double y = msin * e.HorizontalChange + mcos * e.VerticalChange;
+
+ App.Locator.Main.PhotoOffsetX += x / App.Locator.Main.PhotoScale;
+ App.Locator.Main.PhotoOffsetY += y / App.Locator.Main.PhotoScale;
+ }
+ }
+
+ private void gl_PinchDelta(object sender, PinchGestureEventArgs e)
+ {
+ if (App.Locator.Main.IsBackgroundImageEdit && App.Locator.Main.HasPhoto)
+ {
+ App.Locator.Main.PhotoAngle = _initPhotoRotation + e.TotalAngleDelta;
+ App.Locator.Main.PhotoScale = _initPhotoScale * e.DistanceRatio;
+ }
+ }
+
+ private double _initPhotoRotation;
+ private double _initPhotoScale;
+
+ private void gl_PinchStarted(object sender, PinchStartedGestureEventArgs e)
+ {
+ if (App.Locator.Main.IsBackgroundImageEdit && App.Locator.Main.HasPhoto)
+ {
+ _initPhotoRotation = App.Locator.Main.PhotoAngle;
+ _initPhotoScale = App.Locator.Main.PhotoScale;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/AppSettings.cs b/CompassVO/Model/AppSettings.cs
new file mode 100644
index 0000000..22cbc74
--- /dev/null
+++ b/CompassVO/Model/AppSettings.cs
@@ -0,0 +1,158 @@
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization;
+
+namespace CompassVO.Model
+{
+ [DataContract]
+ public class AppSettings
+ {
+ [IgnoreDataMember]
+ public const string APP_SETTINGS_KEY = "APP_SETTINGS_KEY";
+
+ private double _dialAngle;
+
+ [DataMember]
+ public double DialAngle
+ {
+ get { return _dialAngle; }
+ set
+ {
+ if (_dialAngle == value)
+ return;
+ _dialAngle = value;
+ }
+ }
+
+ private string _currentTheme;
+
+ [DataMember]
+ public string CurrentTheme
+ {
+ get { return _currentTheme; }
+ set
+ {
+ if (_currentTheme == value)
+ return;
+ _currentTheme = value;
+ }
+ }
+
+ private double _photoOffsetX = 0;
+
+ [DataMember]
+ public double PhotoOffsetX
+ {
+ get { return _photoOffsetX; }
+ set
+ {
+ if (_photoOffsetX == value)
+ return;
+ _photoOffsetX = value;
+ }
+ }
+
+ private double _photoOffsetY = 0;
+
+ [DataMember]
+ public double PhotoOffsetY
+ {
+ get { return _photoOffsetY; }
+ set
+ {
+ if (_photoOffsetY == value)
+ return;
+ _photoOffsetY = value;
+ }
+ }
+
+ private double _photoScale = 1;
+
+ [DataMember]
+ public double PhotoScale
+ {
+ get { return _photoScale; }
+ set
+ {
+ if (_photoScale == value)
+ return;
+ _photoScale = value;
+ }
+ }
+
+ private double _photoAngle = 0;
+
+ [DataMember]
+ public double PhotoAngle
+ {
+ get { return _photoAngle; }
+ set
+ {
+ if (_photoAngle == value)
+ return;
+ _photoAngle = value;
+ }
+ }
+
+ private bool _isHeadingMagnetic = true;
+
+ [DataMember]
+ public bool IsHeadingMagnetic
+ {
+ get { return _isHeadingMagnetic; }
+ set
+ {
+ if (_isHeadingMagnetic == value)
+ return;
+ _isHeadingMagnetic = value;
+ }
+ }
+
+ private AppSettings()
+ {
+ }
+
+ private static AppSettings _instance;
+
+ public static AppSettings Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ if (IsolatedStorageSettings.ApplicationSettings.Contains(APP_SETTINGS_KEY))
+ _instance = IsolatedStorageSettings.ApplicationSettings[APP_SETTINGS_KEY] as AppSettings;
+ else
+ _instance = new AppSettings();
+ }
+ return _instance;
+ }
+ }
+
+ public static object _lock = new object();
+
+ public static void Save()
+ {
+ lock (_lock)
+ {
+ if (IsolatedStorageSettings.ApplicationSettings.Contains(APP_SETTINGS_KEY))
+ {
+ IsolatedStorageSettings.ApplicationSettings[APP_SETTINGS_KEY] = AppSettings.Instance;
+ }
+ else
+ {
+ IsolatedStorageSettings.ApplicationSettings.Add(APP_SETTINGS_KEY, AppSettings.Instance);
+ }
+ IsolatedStorageSettings.ApplicationSettings.Save();
+ }
+ }
+
+ public static void Load()
+ {
+ lock (_lock)
+ {
+ if (IsolatedStorageSettings.ApplicationSettings.Contains(APP_SETTINGS_KEY))
+ _instance = IsolatedStorageSettings.ApplicationSettings[APP_SETTINGS_KEY] as AppSettings;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Constants.cs b/CompassVO/Model/Constants.cs
new file mode 100644
index 0000000..9bd991e
--- /dev/null
+++ b/CompassVO/Model/Constants.cs
@@ -0,0 +1,13 @@
+namespace CompassVO.Model
+{
+ public class Constants
+ {
+ public const string WEB_PAGE = "http://www.just-windows-phone.com";
+ public const string WEB_PAGE_VO = "http://www.vorienteering.com";
+ public const string JWP_SUPPORT_EMAIL = "justwindowsphone@gmail.com";
+
+ public const string FINAL_PHOTO_FILENAME = "selectedPhoto.jpg";
+ public const string TEMP_PHOTO_FILENAME = "tempPhoto.jpg";
+ public const string PHOTO_FOLDER = "photo";
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Container.cs b/CompassVO/Model/Container.cs
new file mode 100644
index 0000000..ac02e70
--- /dev/null
+++ b/CompassVO/Model/Container.cs
@@ -0,0 +1,31 @@
+using MicroIoc;
+
+namespace CompassVO.Model
+{
+ public sealed class Container
+ {
+ private Container()
+ {
+ }
+
+ public static IMicroIocContainer Instance
+ {
+ get
+ {
+ return Nested.instance;
+ }
+ }
+
+ private class Nested
+ {
+ // Explicit static constructor to tell C# compiler
+ // not to mark type as before field init
+ static Nested()
+ {
+ }
+
+ internal static readonly IMicroIocContainer instance =
+ new MicroIocContainer();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Messages/CompassThemeChangedMessage.cs b/CompassVO/Model/Messages/CompassThemeChangedMessage.cs
new file mode 100644
index 0000000..0a7c2b9
--- /dev/null
+++ b/CompassVO/Model/Messages/CompassThemeChangedMessage.cs
@@ -0,0 +1,6 @@
+namespace CompassVO.Model.Messages
+{
+ public class CompassThemeChangedMessage
+ {
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Messages/LoadPhotoMessage.cs b/CompassVO/Model/Messages/LoadPhotoMessage.cs
new file mode 100644
index 0000000..3ad0ade
--- /dev/null
+++ b/CompassVO/Model/Messages/LoadPhotoMessage.cs
@@ -0,0 +1,6 @@
+namespace CompassVO.Model.Messages
+{
+ public class LoadPhotoMessage
+ {
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Messages/RefreshAppBarMessage.cs b/CompassVO/Model/Messages/RefreshAppBarMessage.cs
new file mode 100644
index 0000000..45939cb
--- /dev/null
+++ b/CompassVO/Model/Messages/RefreshAppBarMessage.cs
@@ -0,0 +1,6 @@
+namespace CompassVO.Model.Messages
+{
+ public class RefreshAppBarMessage
+ {
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Messages/ShowCameraPreviewMessage.cs b/CompassVO/Model/Messages/ShowCameraPreviewMessage.cs
new file mode 100644
index 0000000..7b62dfe
--- /dev/null
+++ b/CompassVO/Model/Messages/ShowCameraPreviewMessage.cs
@@ -0,0 +1,12 @@
+namespace CompassVO.Model.Messages
+{
+ public class ShowCameraPreviewMessage
+ {
+ public bool ShowCamera { get; set; }
+
+ public ShowCameraPreviewMessage(bool showCamera)
+ {
+ ShowCamera = showCamera;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Messages/TakePhotoMessage.cs b/CompassVO/Model/Messages/TakePhotoMessage.cs
new file mode 100644
index 0000000..3c10dc4
--- /dev/null
+++ b/CompassVO/Model/Messages/TakePhotoMessage.cs
@@ -0,0 +1,6 @@
+namespace CompassVO.Model.Messages
+{
+ public class TakePhotoMessage
+ {
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/Messages/UpdateCompassUIMessage.cs b/CompassVO/Model/Messages/UpdateCompassUIMessage.cs
new file mode 100644
index 0000000..10f67c9
--- /dev/null
+++ b/CompassVO/Model/Messages/UpdateCompassUIMessage.cs
@@ -0,0 +1,16 @@
+namespace CompassVO.Model.Messages
+{
+ public class UpdateCompassUIMessage
+ {
+ private UpdateCompassUIMessage()
+ {
+ }
+
+ public UpdateCompassUIMessage(double angle)
+ {
+ Angle = angle;
+ }
+
+ public double Angle { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/OperationModeEnum.cs b/CompassVO/Model/OperationModeEnum.cs
new file mode 100644
index 0000000..fdcce72
--- /dev/null
+++ b/CompassVO/Model/OperationModeEnum.cs
@@ -0,0 +1,9 @@
+namespace CompassVO.Model
+{
+ public enum OperationModeEnum
+ {
+ CompassMode,
+ DialEdit,
+ PhotoEdit
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Model/QuitException.cs b/CompassVO/Model/QuitException.cs
new file mode 100644
index 0000000..01a2181
--- /dev/null
+++ b/CompassVO/Model/QuitException.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace CompassVO.Model
+{
+ public class QuitException : Exception
+ {
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Properties/AppManifest.xml b/CompassVO/Properties/AppManifest.xml
new file mode 100644
index 0000000..161cabc
--- /dev/null
+++ b/CompassVO/Properties/AppManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Properties/AssemblyInfo.cs b/CompassVO/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..b9fd0a7
--- /dev/null
+++ b/CompassVO/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Compass VO")]
+[assembly: AssemblyDescription("Compass application for all users.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Just Windows Phone")]
+[assembly: AssemblyProduct("Compass VO")]
+[assembly: AssemblyCopyright("Copyright © Just Windows Phone 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("dd9d3d8f-4ce9-4b41-8a35-0cd127192ade")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.2.0")]
+[assembly: AssemblyFileVersion("1.2.0")]
+[assembly: NeutralResourcesLanguageAttribute("en-US")]
\ No newline at end of file
diff --git a/CompassVO/Properties/WMAppManifest.xml b/CompassVO/Properties/WMAppManifest.xml
new file mode 100644
index 0000000..27b5eea
--- /dev/null
+++ b/CompassVO/Properties/WMAppManifest.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ ApplicationIcon.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Background.png
+ 0
+ Background.png
+ Compass VO
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Resources/Fonts/LCDMU.TTF b/CompassVO/Resources/Fonts/LCDMU.TTF
new file mode 100644
index 0000000..d5eab90
Binary files /dev/null and b/CompassVO/Resources/Fonts/LCDMU.TTF differ
diff --git a/CompassVO/Service/CompassEx.cs b/CompassVO/Service/CompassEx.cs
new file mode 100644
index 0000000..b5c23ff
--- /dev/null
+++ b/CompassVO/Service/CompassEx.cs
@@ -0,0 +1,186 @@
+using CompassVO.Utils.Filters;
+using Microsoft.Devices.Sensors;
+using System;
+
+namespace CompassVO.Service
+{
+ public class CompassEx : IDisposable
+ {
+ private Compass _compass;
+ private bool _needsCalibration;
+ private SimpleKalman filteredHeading = new SimpleKalman();
+
+ #region static Properties
+
+ private static bool isInit;
+ private static bool _isSupported;
+
+ public static bool IsSupported
+ {
+ get
+ {
+ if (!isInit)
+ {
+ _isSupported = Compass.IsSupported;
+ isInit = true;
+ }
+ return _isSupported;
+ }
+ set { _isSupported = value; }
+ }
+
+ #endregion static Properties
+
+ #region public properties
+
+ public TimeSpan TimeBetweenUpdates { get; set; }
+
+ private CompassReadingEx _currentValue = new CompassReadingEx();
+
+ public CompassReadingEx CurrentValue
+ {
+ get
+ {
+ return _currentValue;
+ }
+ }
+
+ public bool IsDataValid
+ {
+ get { return _compass != null ? _compass.IsDataValid : false; }
+ }
+
+ #endregion public properties
+
+ #region public events
+
+ public event System.EventHandler Calibrate;
+
+ public event System.EventHandler Calibrated;
+
+ public event System.EventHandler> CurrentValueChanged;
+
+ #endregion public events
+
+ public CompassEx()
+ {
+ _compass = new Compass();
+ _compass.Calibrate += new EventHandler(_compass_Calibrate);
+ _compass.CurrentValueChanged += new EventHandler>(_compass_CurrentValueChanged);
+ }
+
+ private void _compass_CurrentValueChanged(object sender, SensorReadingEventArgs e)
+ {
+ if (_needsCalibration && e.SensorReading.HeadingAccuracy <= 20)
+ {
+ _needsCalibration = false;
+ Calibrated?.Invoke(sender, new CalibrationEventArgs());
+ }
+
+ if (_compass != null)
+ {
+ ProcessCurrentValue(e.SensorReading);
+ }
+
+ if (CurrentValueChanged != null)
+ {
+ CurrentValueChanged(sender, new SensorReadingEventArgs() { SensorReading = CurrentValue });
+ }
+ }
+
+ private void _compass_Calibrate(object sender, CalibrationEventArgs e)
+ {
+ _needsCalibration = true;
+ Calibrate?.Invoke(sender, e);
+ }
+
+ private bool isFirstReading;
+ private double md;
+
+ private void ProcessCurrentValue(CompassReading compassReading)
+ {
+ double newmd = compassReading.TrueHeading - compassReading.MagneticHeading;
+ if (Math.Abs(newmd) < 45)
+ {
+ md = newmd;
+ }
+
+ if (isFirstReading)
+ {
+ filteredHeading.init(compassReading.TrueHeading);
+ isFirstReading = false;
+ }
+
+ double currentFilter = filteredHeading.current();
+ double currentRead = compassReading.TrueHeading - md;
+ if (Math.Abs(currentRead - currentFilter) > 180)
+ {
+ if (currentRead > currentFilter)
+ currentRead -= 360;
+ else if (currentRead < currentFilter)
+ currentRead += 360;
+
+ if (Math.Sign(currentRead) == Math.Sign(currentFilter))
+ {
+ if (currentRead < 0)
+ {
+ currentRead += 360;
+ currentFilter += 360;
+ filteredHeading.init(currentFilter);
+ }
+ else if (currentRead > 360 && currentFilter > 360)
+ {
+ currentRead -= 360;
+ currentFilter -= 360;
+ filteredHeading.init(currentFilter);
+ }
+ }
+ }
+
+ currentRead = filteredHeading.update(currentRead);
+ currentRead = (currentRead + 360) % 360;
+ double trueRead = ((currentRead + md) + 360) % 360;
+
+ _currentValue = new CompassReadingEx()
+ {
+ HeadingAccuracy = compassReading.HeadingAccuracy,
+ TrueHeading = trueRead,
+ MagneticHeading = currentRead,
+ MagneticDeclination = md,
+ MagnetometerReading = compassReading.MagnetometerReading,
+ Timestamp = compassReading.Timestamp,
+ MagneticCardinalDirection = CompassReadingEx.GetCardinalDirection(currentRead),
+ TrueCardinalDirection = CompassReadingEx.GetCardinalDirection(trueRead)
+ };
+ }
+
+ #region public methods
+
+ public void Start()
+ {
+ if (IsSupported)
+ {
+ _compass.TimeBetweenUpdates = this.TimeBetweenUpdates;
+ _compass.Start();
+ }
+ }
+
+ public void Stop()
+ {
+ if (_compass != null)
+ _compass.Stop();
+ }
+
+ #endregion public methods
+
+ #region Implementation of IDisposable
+
+ public void Dispose()
+ {
+ if (_compass != null)
+ _compass.Dispose();
+ }
+
+ #endregion Implementation of IDisposable
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Service/CompassReadingEx.cs b/CompassVO/Service/CompassReadingEx.cs
new file mode 100644
index 0000000..1dd5ad2
--- /dev/null
+++ b/CompassVO/Service/CompassReadingEx.cs
@@ -0,0 +1,108 @@
+using Microsoft.Devices.Sensors;
+using Microsoft.Xna.Framework;
+using System;
+
+namespace CompassVO.Service
+{
+ public struct CompassReadingEx : ISensorReading
+ {
+ public DateTimeOffset Timestamp
+ {
+ get;
+ internal set;
+ }
+
+ public double HeadingAccuracy
+ {
+ get;
+ internal set;
+ }
+
+ public double TrueHeading
+ {
+ get;
+ internal set;
+ }
+
+ public Vector3 MagnetometerReading
+ {
+ get;
+ internal set;
+ }
+
+ public double MagneticHeading
+ {
+ get;
+ internal set;
+ }
+
+ public double MagneticDeclination
+ {
+ get;
+ internal set;
+ }
+
+ public CardinalDirection MagneticCardinalDirection
+ {
+ get;
+ internal set;
+ }
+
+ public CardinalDirection TrueCardinalDirection
+ {
+ get;
+ internal set;
+ }
+
+ public static CardinalDirection GetCardinalDirection(double angle)
+ {
+ angle = angle % 360;
+ if (angle < 22.5 || angle >= 337.5)
+ {
+ return CardinalDirection.N;
+ }
+ if (angle >= 22.5 && angle < 67.5)
+ {
+ return CardinalDirection.NE;
+ }
+ if (angle >= 67.5 && angle < 112.5)
+ {
+ return CardinalDirection.E;
+ }
+ if (angle >= 112.5 && angle < 157.5)
+ {
+ return CardinalDirection.SE;
+ }
+ if (angle >= 157.5 && angle < 202.5)
+ {
+ return CardinalDirection.S;
+ }
+ if (angle >= 202.5 && angle < 247.5)
+ {
+ return CardinalDirection.SW;
+ }
+ if (angle >= 247.5 && angle < 292.5)
+ {
+ return CardinalDirection.W;
+ }
+ if (angle >= 292.5 && angle < 337.5)
+ {
+ return CardinalDirection.NW;
+ }
+ return CardinalDirection.Unknow;
+ }
+ }
+
+ public enum CardinalDirection
+ {
+ Unknow,
+ N,
+ S,
+ E,
+ W,
+ NE,
+ SE,
+ NW,
+ SW
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Service/CompassThemeController.cs b/CompassVO/Service/CompassThemeController.cs
new file mode 100644
index 0000000..3d91db7
--- /dev/null
+++ b/CompassVO/Service/CompassThemeController.cs
@@ -0,0 +1,42 @@
+using CompassVO.Themes;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace CompassVO.Service
+{
+ public class CompassThemeController
+ {
+ private static CompassThemeController _instance;
+
+ public static CompassThemeController Instance
+ {
+ get
+ {
+ if (_instance == null)
+ _instance = new CompassThemeController();
+ return _instance;
+ }
+ }
+
+ private List _allThemes;
+
+ public List GetAllThemes()
+ {
+ if (_allThemes == null)
+ {
+ _allThemes = new List();
+ foreach (Type appType in Assembly.GetExecutingAssembly().GetTypes())
+ {
+ if (appType != typeof(ICompassTheme) && appType.GetInterface(typeof(ICompassTheme).FullName, false) != null)
+ {
+ _allThemes.Add((ICompassTheme)Activator.CreateInstance(appType));
+ }
+ }
+ _allThemes.Sort((a, b) => { return a.Order.CompareTo(b.Order); });
+ }
+
+ return _allThemes;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Service/Navigation/IPageNavigation.cs b/CompassVO/Service/Navigation/IPageNavigation.cs
new file mode 100644
index 0000000..8f022a4
--- /dev/null
+++ b/CompassVO/Service/Navigation/IPageNavigation.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Windows.Navigation;
+
+namespace CompassVO.Service.Navigation
+{
+ public interface INavigationService
+ {
+ event NavigatingCancelEventHandler Navigating;
+
+ object CurrentContext { get; }
+
+ void NavigateTo(Uri pageUri);
+
+ void NavigateTo(Uri pageUri, object context);
+
+ void GoBack();
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Service/Navigation/PageNavigation.cs b/CompassVO/Service/Navigation/PageNavigation.cs
new file mode 100644
index 0000000..86f2e7e
--- /dev/null
+++ b/CompassVO/Service/Navigation/PageNavigation.cs
@@ -0,0 +1,81 @@
+using Microsoft.Phone.Controls;
+using System;
+using System.Windows;
+using System.Windows.Navigation;
+
+namespace CompassVO.Service.Navigation
+{
+ public class NavigationService : INavigationService
+ {
+ private PhoneApplicationFrame mainFrame;
+
+ private bool EnsureMainFrame()
+ {
+ if (mainFrame != null)
+ {
+ return true;
+ }
+ mainFrame = Application.Current.RootVisual as PhoneApplicationFrame;
+ if (mainFrame != null)
+ {
+ // Could be null if the app runs inside a design tool
+ mainFrame.Navigating += (s, e) =>
+ {
+ if (Navigating != null)
+ {
+ Navigating(s, e);
+ }
+ };
+ return true;
+ }
+ return false;
+ }
+
+ #region INavigationService implementation
+
+ public event NavigatingCancelEventHandler Navigating;
+
+ private object currentContext;
+
+ public object CurrentContext
+ {
+ get { return this.currentContext; }
+ }
+
+ public void NavigateTo(Uri pageUri)
+ {
+ if (pageUri == null)
+ throw new ArgumentNullException("uri");
+ if (EnsureMainFrame())
+ this.NavigateTo(pageUri, null);
+ }
+
+ public void NavigateTo(Uri pageUri, object context)
+ {
+ if (pageUri == null)
+ throw new ArgumentNullException("uri");
+ if (EnsureMainFrame())
+ {
+ this.currentContext = context;
+ mainFrame.Navigate(pageUri);
+ }
+ }
+
+ public void GoBack()
+ {
+ if (EnsureMainFrame() && mainFrame.CanGoBack)
+ {
+ try
+ {
+ mainFrame.GoBack();
+ }
+ catch (Exception)
+ {
+ //
+ }
+ }
+ }
+
+ #endregion INavigationService implementation
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/SettingsPage.xaml b/CompassVO/SettingsPage.xaml
new file mode 100644
index 0000000..3d1cddd
--- /dev/null
+++ b/CompassVO/SettingsPage.xaml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/SettingsPage.xaml.cs b/CompassVO/SettingsPage.xaml.cs
new file mode 100644
index 0000000..8bb2dcd
--- /dev/null
+++ b/CompassVO/SettingsPage.xaml.cs
@@ -0,0 +1,27 @@
+using Microsoft.Phone.Controls;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace CompassVO
+{
+ public partial class SettingsPage : PhoneApplicationPage
+ {
+ private bool isPageLoaded;
+
+ public SettingsPage()
+ {
+ InitializeComponent();
+ }
+
+ private void RadDataBoundListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (isPageLoaded && NavigationService.CanGoBack)
+ NavigationService.GoBack();
+ }
+
+ private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
+ {
+ isPageLoaded = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Sounds/camera.wav b/CompassVO/Sounds/camera.wav
new file mode 100644
index 0000000..e9c185d
Binary files /dev/null and b/CompassVO/Sounds/camera.wav differ
diff --git a/CompassVO/SplashScreenImage.jpg b/CompassVO/SplashScreenImage.jpg
new file mode 100644
index 0000000..19f737b
Binary files /dev/null and b/CompassVO/SplashScreenImage.jpg differ
diff --git a/CompassVO/Themes/CompassUiUtil.cs b/CompassVO/Themes/CompassUiUtil.cs
new file mode 100644
index 0000000..344acf7
--- /dev/null
+++ b/CompassVO/Themes/CompassUiUtil.cs
@@ -0,0 +1,70 @@
+using CompassVO.Model.Messages;
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+
+namespace CompassVO.Themes
+{
+ public static class CompassUiUtil
+ {
+ private static Storyboard sb;
+ public static RotateTransform needleRotation;
+ private static UserControl CompassUi;
+
+ public static void UpdateCompassUI(UpdateCompassUIMessage res, UserControl compassUi)
+ {
+ UpdateUI(res.Angle, compassUi);
+ }
+
+ private static void SetControl(UserControl compassUi)
+ {
+ CompassUi = compassUi;
+ needleRotation = compassUi.FindName("needleRotation") as RotateTransform;
+ }
+
+ public static void UpdateUI(double angle, UserControl compassUi)
+ {
+ SetControl(compassUi);
+
+ if (sb != null)
+ sb.SkipToFill();
+
+ double newAngle = angle;
+ needleRotation.Angle = needleRotation.Angle % 360;
+ double diff = newAngle - needleRotation.Angle;
+ if (Math.Abs(diff) > 180)
+ newAngle += -Math.Sign(diff) * 360;
+ sb = StoryBoardEffect(newAngle, 50);
+ sb.Begin();
+ }
+
+ #region StoryBoard
+
+ private static Storyboard StoryBoardEffect(double needleRotationAngle, int miliseconds)
+ {
+ //add new storyboard and animation
+ Storyboard sb = new Storyboard();
+ if (needleRotation != null)
+ {
+ #region ellipse width
+
+ DoubleAnimation daNeedle = new DoubleAnimation
+ {
+ To = needleRotationAngle,
+ Duration = new Duration(TimeSpan.FromMilliseconds(miliseconds))
+ };
+ Storyboard.SetTarget(daNeedle, needleRotation);
+ PropertyPath myPropertyPath = new PropertyPath(RotateTransform.AngleProperty);
+ Storyboard.SetTargetProperty(daNeedle, myPropertyPath);
+ sb.Children.Add(daNeedle);
+
+ #endregion ellipse width
+ }
+ return sb;
+ }
+
+ #endregion StoryBoard
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/Generic.xaml b/CompassVO/Themes/Generic.xaml
new file mode 100644
index 0000000..6780511
--- /dev/null
+++ b/CompassVO/Themes/Generic.xaml
@@ -0,0 +1,56 @@
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Themes/ICompassTheme.cs b/CompassVO/Themes/ICompassTheme.cs
new file mode 100644
index 0000000..0e2eeee
--- /dev/null
+++ b/CompassVO/Themes/ICompassTheme.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace CompassVO.Themes
+{
+ public interface ICompassTheme
+ {
+ string ThemeName { get; }
+ string ThemeImageUrl { get; }
+ Color BackgroundColor { get; }
+ Color ForegroundColor { get; }
+ int Order { get; }
+
+ bool SupportsDialRotation { get; }
+ bool SupportsBackgroundImages { get; }
+ bool SupportsTargets { get; }
+ Type UIcomponent { get; }
+
+ double GetAngle(Point point);
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_cardinal/compass_cardinal_UI.xaml b/CompassVO/Themes/compass_cardinal/compass_cardinal_UI.xaml
new file mode 100644
index 0000000..4f150f4
--- /dev/null
+++ b/CompassVO/Themes/compass_cardinal/compass_cardinal_UI.xaml
@@ -0,0 +1,90 @@
+
+
+
+
+ magnetic heading:
+
+
+ °
+
+
+ true heading:
+
+
+ °
+
+
+ magnetic declination:
+
+
+ °
+
+ accuracy:
+
+
+ °
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_cardinal/compass_cardinal_UI.xaml.cs b/CompassVO/Themes/compass_cardinal/compass_cardinal_UI.xaml.cs
new file mode 100644
index 0000000..f82eec7
--- /dev/null
+++ b/CompassVO/Themes/compass_cardinal/compass_cardinal_UI.xaml.cs
@@ -0,0 +1,22 @@
+using CompassVO.Model.Messages;
+using CompassVO.Utils;
+using GalaSoft.MvvmLight.Messaging;
+using System;
+using System.Windows.Controls;
+using System.Windows.Media.Imaging;
+
+namespace CompassVO.Themes.compass_cardinal
+{
+ public partial class compass_cardinal_UI : UserControl
+ {
+ public compass_cardinal_UI()
+ {
+ InitializeComponent();
+
+ imgCompass.Source = new BitmapImage(new Uri(PhoneThemeDetect.IsDarkTheme() ? @"images\compass_old_needle.png" : @"images\compass_old_needle_light.png", UriKind.Relative));
+
+ CompassUiUtil.UpdateUI(0, this);
+ Messenger.Default.Register(this, (res) => CompassUiUtil.UpdateCompassUI(res, this));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_cardinal/images/compass_old_needle.png b/CompassVO/Themes/compass_cardinal/images/compass_old_needle.png
new file mode 100644
index 0000000..26c7377
Binary files /dev/null and b/CompassVO/Themes/compass_cardinal/images/compass_old_needle.png differ
diff --git a/CompassVO/Themes/compass_cardinal/images/compass_old_needle_light.png b/CompassVO/Themes/compass_cardinal/images/compass_old_needle_light.png
new file mode 100644
index 0000000..517c8fc
Binary files /dev/null and b/CompassVO/Themes/compass_cardinal/images/compass_old_needle_light.png differ
diff --git a/CompassVO/Themes/compass_cardinal/images/sample_old.png b/CompassVO/Themes/compass_cardinal/images/sample_old.png
new file mode 100644
index 0000000..4b4843d
Binary files /dev/null and b/CompassVO/Themes/compass_cardinal/images/sample_old.png differ
diff --git a/CompassVO/Themes/compass_cardinal/theme_compass_cardinal.cs b/CompassVO/Themes/compass_cardinal/theme_compass_cardinal.cs
new file mode 100644
index 0000000..8d6e67b
--- /dev/null
+++ b/CompassVO/Themes/compass_cardinal/theme_compass_cardinal.cs
@@ -0,0 +1,88 @@
+using CompassVO.Utils;
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace CompassVO.Themes.compass_cardinal
+{
+ public class theme_compass_cardinal : ICompassTheme
+ {
+ public theme_compass_cardinal()
+ {
+ }
+
+ #region Implementation of ICompassTheme
+
+ public Color BackgroundColor
+ {
+ get
+ {
+ return PhoneThemeDetect.IsDarkTheme() ? PhoneThemeDetect.DarkThemeBackground : PhoneThemeDetect.LightThemeBackground;
+ }
+ }
+
+ public Color ForegroundColor
+ {
+ get
+ {
+ return PhoneThemeDetect.IsDarkTheme() ? PhoneThemeDetect.LightThemeBackground : PhoneThemeDetect.DarkThemeBackground;
+ }
+ }
+
+ public int Order
+ {
+ get { return 2; }
+ }
+
+ public bool SupportsDialRotation
+ {
+ get { return false; }
+ }
+
+ public bool SupportsBackgroundImages
+ {
+ get { return true; }
+ }
+
+ public bool SupportsTargets
+ {
+ get { return true; }
+ }
+
+ public Type UIcomponent
+ {
+ get { return typeof(compass_cardinal_UI); }
+ }
+
+ private Point needleCenter = new Point(240, 295);
+
+ public double GetAngle(Point point)
+ {
+ return Angle(needleCenter, point);
+ }
+
+ private const double Rad2Deg = 180.0 / Math.PI;
+ private const double Deg2Rad = Math.PI / 180.0;
+
+ private double Angle(Point start, Point end)
+ {
+ return Math.Atan2(start.Y - end.Y, end.X - start.X) * Rad2Deg;
+ }
+
+ #endregion Implementation of ICompassTheme
+
+ #region ICompassTheme Members
+
+ public string ThemeName
+ {
+ get { return "Cardinal rose"; }
+ }
+
+ public string ThemeImageUrl
+ {
+ get { return "/Themes/compass_cardinal/images/sample_old.png"; }
+ }
+
+ #endregion ICompassTheme Members
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_digital/compass_digital_UI.xaml b/CompassVO/Themes/compass_digital/compass_digital_UI.xaml
new file mode 100644
index 0000000..a2396a3
--- /dev/null
+++ b/CompassVO/Themes/compass_digital/compass_digital_UI.xaml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ °
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_digital/compass_digital_UI.xaml.cs b/CompassVO/Themes/compass_digital/compass_digital_UI.xaml.cs
new file mode 100644
index 0000000..00ce38b
--- /dev/null
+++ b/CompassVO/Themes/compass_digital/compass_digital_UI.xaml.cs
@@ -0,0 +1,16 @@
+using CompassVO.Model.Messages;
+using GalaSoft.MvvmLight.Messaging;
+using System.Windows.Controls;
+
+namespace CompassVO.Themes.compass_digital
+{
+ public partial class compass_digital_UI : UserControl
+ {
+ public compass_digital_UI()
+ {
+ InitializeComponent();
+ CompassUiUtil.UpdateUI(0, this);
+ Messenger.Default.Register(this, (res) => CompassUiUtil.UpdateCompassUI(res, this));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_digital/images/compass_digital_dial.png b/CompassVO/Themes/compass_digital/images/compass_digital_dial.png
new file mode 100644
index 0000000..a4cd636
Binary files /dev/null and b/CompassVO/Themes/compass_digital/images/compass_digital_dial.png differ
diff --git a/CompassVO/Themes/compass_digital/images/compass_digital_needle.png b/CompassVO/Themes/compass_digital/images/compass_digital_needle.png
new file mode 100644
index 0000000..707554b
Binary files /dev/null and b/CompassVO/Themes/compass_digital/images/compass_digital_needle.png differ
diff --git a/CompassVO/Themes/compass_digital/images/sample_digital.png b/CompassVO/Themes/compass_digital/images/sample_digital.png
new file mode 100644
index 0000000..918b7cb
Binary files /dev/null and b/CompassVO/Themes/compass_digital/images/sample_digital.png differ
diff --git a/CompassVO/Themes/compass_digital/theme_compass_digital.cs b/CompassVO/Themes/compass_digital/theme_compass_digital.cs
new file mode 100644
index 0000000..2b5786b
--- /dev/null
+++ b/CompassVO/Themes/compass_digital/theme_compass_digital.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace CompassVO.Themes.compass_digital
+{
+ public class theme_compass_digital : ICompassTheme
+ {
+ public theme_compass_digital()
+ {
+ }
+
+ #region Implementation of ICompassTheme
+
+ public Color BackgroundColor
+ {
+ get
+ {
+ return Colors.LightGray;
+ }
+ }
+
+ public Color ForegroundColor
+ {
+ get
+ {
+ return Color.FromArgb(255, 80, 80, 80);
+ }
+ }
+
+ public int Order
+ {
+ get { return 1; }
+ }
+
+ public bool SupportsDialRotation
+ {
+ get { return true; }
+ }
+
+ public bool SupportsBackgroundImages
+ {
+ get { return true; }
+ }
+
+ public bool SupportsTargets
+ {
+ get { return true; }
+ }
+
+ public Type UIcomponent
+ {
+ get { return typeof(compass_digital_UI); }
+ }
+
+ private Point needleCenter = new Point(240, 408);
+
+ public double GetAngle(Point point)
+ {
+ return Angle(needleCenter, point);
+ }
+
+ private const double Rad2Deg = 180.0 / Math.PI;
+ private const double Deg2Rad = Math.PI / 180.0;
+
+ private double Angle(Point start, Point end)
+ {
+ return Math.Atan2(start.Y - end.Y, end.X - start.X) * Rad2Deg;
+ }
+
+ #endregion Implementation of ICompassTheme
+
+ #region ICompassTheme Members
+
+ public string ThemeName
+ {
+ get { return "Digital"; }
+ }
+
+ public string ThemeImageUrl
+ {
+ get { return "/Themes/compass_digital/images/sample_digital.png"; }
+ }
+
+ #endregion ICompassTheme Members
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_metro_graphic/compass_metro_graphic_UI.xaml b/CompassVO/Themes/compass_metro_graphic/compass_metro_graphic_UI.xaml
new file mode 100644
index 0000000..b143f0f
--- /dev/null
+++ b/CompassVO/Themes/compass_metro_graphic/compass_metro_graphic_UI.xaml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+ compass heading
+
+
+ °
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_metro_graphic/compass_metro_graphic_UI.xaml.cs b/CompassVO/Themes/compass_metro_graphic/compass_metro_graphic_UI.xaml.cs
new file mode 100644
index 0000000..3b1c1ce
--- /dev/null
+++ b/CompassVO/Themes/compass_metro_graphic/compass_metro_graphic_UI.xaml.cs
@@ -0,0 +1,16 @@
+using CompassVO.Model.Messages;
+using GalaSoft.MvvmLight.Messaging;
+using System.Windows.Controls;
+
+namespace CompassVO.Themes.compass_orienteering
+{
+ public partial class compass_metro_graphic_UI : UserControl
+ {
+ public compass_metro_graphic_UI()
+ {
+ InitializeComponent();
+ CompassUiUtil.UpdateUI(0, this);
+ Messenger.Default.Register(this, (res) => CompassUiUtil.UpdateCompassUI(res, this));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_metro_graphic/images/sample_metro_graphic.png b/CompassVO/Themes/compass_metro_graphic/images/sample_metro_graphic.png
new file mode 100644
index 0000000..e61c2f8
Binary files /dev/null and b/CompassVO/Themes/compass_metro_graphic/images/sample_metro_graphic.png differ
diff --git a/CompassVO/Themes/compass_metro_graphic/theme_compass_metro_graphic.cs b/CompassVO/Themes/compass_metro_graphic/theme_compass_metro_graphic.cs
new file mode 100644
index 0000000..e12cee6
--- /dev/null
+++ b/CompassVO/Themes/compass_metro_graphic/theme_compass_metro_graphic.cs
@@ -0,0 +1,89 @@
+using CompassVO.Themes.compass_orienteering;
+using CompassVO.Utils;
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace CompassVO.Themes.compass_metro_graphic
+{
+ public class theme_compass_metro_graphic : ICompassTheme
+ {
+ public theme_compass_metro_graphic()
+ {
+ }
+
+ #region Implementation of ICompassTheme
+
+ public Color BackgroundColor
+ {
+ get
+ {
+ return PhoneThemeDetect.IsDarkTheme() ? PhoneThemeDetect.DarkThemeBackground : PhoneThemeDetect.LightThemeBackground;
+ }
+ }
+
+ public Color ForegroundColor
+ {
+ get
+ {
+ return PhoneThemeDetect.IsDarkTheme() ? Color.FromArgb(255, 102, 204, 255) : Colors.Red;
+ }
+ }
+
+ public int Order
+ {
+ get { return 3; }
+ }
+
+ public bool SupportsDialRotation
+ {
+ get { return false; }
+ }
+
+ public bool SupportsBackgroundImages
+ {
+ get { return true; }
+ }
+
+ public bool SupportsTargets
+ {
+ get { return true; }
+ }
+
+ public Type UIcomponent
+ {
+ get { return typeof(compass_metro_graphic_UI); }
+ }
+
+ private Point needleCenter = new Point(240, 500);
+
+ public double GetAngle(Point point)
+ {
+ return Angle(needleCenter, point);
+ }
+
+ private const double Rad2Deg = 180.0 / Math.PI;
+ private const double Deg2Rad = Math.PI / 180.0;
+
+ private double Angle(Point start, Point end)
+ {
+ return Math.Atan2(start.Y - end.Y, end.X - start.X) * Rad2Deg;
+ }
+
+ #endregion Implementation of ICompassTheme
+
+ #region ICompassTheme Members
+
+ public string ThemeName
+ {
+ get { return "Metro graphic"; }
+ }
+
+ public string ThemeImageUrl
+ {
+ get { return "/Themes/compass_metro_graphic/images/sample_metro_graphic.png"; }
+ }
+
+ #endregion ICompassTheme Members
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_night/compass_night_UI.xaml b/CompassVO/Themes/compass_night/compass_night_UI.xaml
new file mode 100644
index 0000000..a2e642e
--- /dev/null
+++ b/CompassVO/Themes/compass_night/compass_night_UI.xaml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+ compass heading
+
+
+ °
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_night/compass_night_UI.xaml.cs b/CompassVO/Themes/compass_night/compass_night_UI.xaml.cs
new file mode 100644
index 0000000..de924bb
--- /dev/null
+++ b/CompassVO/Themes/compass_night/compass_night_UI.xaml.cs
@@ -0,0 +1,16 @@
+using CompassVO.Model.Messages;
+using GalaSoft.MvvmLight.Messaging;
+using System.Windows.Controls;
+
+namespace CompassVO.Themes.compass_night
+{
+ public partial class compass_night_UI : UserControl
+ {
+ public compass_night_UI()
+ {
+ InitializeComponent();
+ CompassUiUtil.UpdateUI(0, this);
+ Messenger.Default.Register(this, (res) => CompassUiUtil.UpdateCompassUI(res, this));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_night/images/sample_night.png b/CompassVO/Themes/compass_night/images/sample_night.png
new file mode 100644
index 0000000..85c1d0f
Binary files /dev/null and b/CompassVO/Themes/compass_night/images/sample_night.png differ
diff --git a/CompassVO/Themes/compass_night/theme_compass_night.cs b/CompassVO/Themes/compass_night/theme_compass_night.cs
new file mode 100644
index 0000000..dbf9843
--- /dev/null
+++ b/CompassVO/Themes/compass_night/theme_compass_night.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace CompassVO.Themes.compass_night
+{
+ public class theme_compass_night : ICompassTheme
+ {
+ public theme_compass_night()
+ {
+ }
+
+ #region Implementation of ICompassTheme
+
+ public Color BackgroundColor
+ {
+ get
+ {
+ return Colors.Black;
+ }
+ }
+
+ public Color ForegroundColor
+ {
+ get
+ {
+ return Color.FromArgb(255, 139, 0, 0);
+ }
+ }
+
+ public int Order
+ {
+ get { return 4; }
+ }
+
+ public bool SupportsDialRotation
+ {
+ get { return false; }
+ }
+
+ public bool SupportsBackgroundImages
+ {
+ get { return true; }
+ }
+
+ public bool SupportsTargets
+ {
+ get { return true; }
+ }
+
+ public Type UIcomponent
+ {
+ get { return typeof(compass_night_UI); }
+ }
+
+ private Point needleCenter = new Point(240, 500);
+
+ public double GetAngle(Point point)
+ {
+ return Angle(needleCenter, point);
+ }
+
+ private const double Rad2Deg = 180.0 / Math.PI;
+ private const double Deg2Rad = Math.PI / 180.0;
+
+ private double Angle(Point start, Point end)
+ {
+ return Math.Atan2(start.Y - end.Y, end.X - start.X) * Rad2Deg;
+ }
+
+ #endregion Implementation of ICompassTheme
+
+ #region ICompassTheme Members
+
+ public string ThemeName
+ {
+ get { return "Night"; }
+ }
+
+ public string ThemeImageUrl
+ {
+ get { return "/Themes/compass_night/images/sample_night.png"; }
+ }
+
+ #endregion ICompassTheme Members
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_orienteering/compass_orienteering_UI.xaml b/CompassVO/Themes/compass_orienteering/compass_orienteering_UI.xaml
new file mode 100644
index 0000000..81be2b6
--- /dev/null
+++ b/CompassVO/Themes/compass_orienteering/compass_orienteering_UI.xaml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_orienteering/compass_orienteering_UI.xaml.cs b/CompassVO/Themes/compass_orienteering/compass_orienteering_UI.xaml.cs
new file mode 100644
index 0000000..ed0f8f3
--- /dev/null
+++ b/CompassVO/Themes/compass_orienteering/compass_orienteering_UI.xaml.cs
@@ -0,0 +1,24 @@
+using CompassVO.Model.Messages;
+using GalaSoft.MvvmLight.Messaging;
+using System.ComponentModel;
+using System.Windows.Controls;
+
+namespace CompassVO.Themes.compass_orienteering
+{
+ public partial class compass_orienteering_UI : UserControl, INotifyPropertyChanged
+ {
+ public compass_orienteering_UI()
+ {
+ this.DataContext = this;
+ InitializeComponent();
+ CompassUiUtil.UpdateUI(0, this);
+ Messenger.Default.Register(this, (res) => CompassUiUtil.UpdateCompassUI(res, this));
+ }
+
+ #region Implementation of INotifyPropertyChanged
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ #endregion Implementation of INotifyPropertyChanged
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Themes/compass_orienteering/images/compass_orienteering_back.png b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_back.png
new file mode 100644
index 0000000..a37b1af
Binary files /dev/null and b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_back.png differ
diff --git a/CompassVO/Themes/compass_orienteering/images/compass_orienteering_dial.png b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_dial.png
new file mode 100644
index 0000000..ecfba74
Binary files /dev/null and b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_dial.png differ
diff --git a/CompassVO/Themes/compass_orienteering/images/compass_orienteering_needle.png b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_needle.png
new file mode 100644
index 0000000..cfed330
Binary files /dev/null and b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_needle.png differ
diff --git a/CompassVO/Themes/compass_orienteering/images/compass_orienteering_rotate.png b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_rotate.png
new file mode 100644
index 0000000..2a54898
Binary files /dev/null and b/CompassVO/Themes/compass_orienteering/images/compass_orienteering_rotate.png differ
diff --git a/CompassVO/Themes/compass_orienteering/images/sample_orienteering.png b/CompassVO/Themes/compass_orienteering/images/sample_orienteering.png
new file mode 100644
index 0000000..1ff02c2
Binary files /dev/null and b/CompassVO/Themes/compass_orienteering/images/sample_orienteering.png differ
diff --git a/CompassVO/Themes/compass_orienteering/theme_compass_orienteering.cs b/CompassVO/Themes/compass_orienteering/theme_compass_orienteering.cs
new file mode 100644
index 0000000..5401f78
--- /dev/null
+++ b/CompassVO/Themes/compass_orienteering/theme_compass_orienteering.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace CompassVO.Themes.compass_orienteering
+{
+ public class theme_compass_orienteering : ICompassTheme
+ {
+ public theme_compass_orienteering()
+ {
+ }
+
+ #region Implementation of ICompassTheme
+
+ public Color BackgroundColor
+ {
+ get { return Colors.White; }
+ }
+
+ public Color ForegroundColor
+ {
+ get
+ {
+ return Colors.Black;
+ }
+ }
+
+ public int Order
+ {
+ get { return 0; }
+ }
+
+ public bool SupportsDialRotation
+ {
+ get { return true; }
+ }
+
+ public bool SupportsBackgroundImages
+ {
+ get { return true; }
+ }
+
+ public bool SupportsTargets
+ {
+ get { return true; }
+ }
+
+ public Type UIcomponent
+ {
+ get { return typeof(compass_orienteering_UI); }
+ }
+
+ private Point needleCenter = new Point(240, 500);
+
+ public double GetAngle(Point point)
+ {
+ return Angle(needleCenter, point);
+ }
+
+ private const double Rad2Deg = 180.0 / Math.PI;
+ private const double Deg2Rad = Math.PI / 180.0;
+
+ private double Angle(Point start, Point end)
+ {
+ return Math.Atan2(start.Y - end.Y, end.X - start.X) * Rad2Deg;
+ }
+
+ #endregion Implementation of ICompassTheme
+
+ #region ICompassTheme Members
+
+ public string ThemeName
+ {
+ get { return "Orienteering"; }
+ }
+
+ public string ThemeImageUrl
+ {
+ get { return "/Themes/compass_orienteering/images/sample_orienteering.png"; }
+ }
+
+ #endregion ICompassTheme Members
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Toolkit.Content/ApplicationBar.Cancel.png b/CompassVO/Toolkit.Content/ApplicationBar.Cancel.png
new file mode 100644
index 0000000..4dd724f
Binary files /dev/null and b/CompassVO/Toolkit.Content/ApplicationBar.Cancel.png differ
diff --git a/CompassVO/Toolkit.Content/ApplicationBar.Check.png b/CompassVO/Toolkit.Content/ApplicationBar.Check.png
new file mode 100644
index 0000000..7a07466
Binary files /dev/null and b/CompassVO/Toolkit.Content/ApplicationBar.Check.png differ
diff --git a/CompassVO/Toolkit.Content/ApplicationBar.Delete.png b/CompassVO/Toolkit.Content/ApplicationBar.Delete.png
new file mode 100644
index 0000000..95bb16d
Binary files /dev/null and b/CompassVO/Toolkit.Content/ApplicationBar.Delete.png differ
diff --git a/CompassVO/Toolkit.Content/ApplicationBar.Select.png b/CompassVO/Toolkit.Content/ApplicationBar.Select.png
new file mode 100644
index 0000000..995deaa
Binary files /dev/null and b/CompassVO/Toolkit.Content/ApplicationBar.Select.png differ
diff --git a/CompassVO/Utils/Filters/SimpleKalman.cs b/CompassVO/Utils/Filters/SimpleKalman.cs
new file mode 100644
index 0000000..bcafdfc
--- /dev/null
+++ b/CompassVO/Utils/Filters/SimpleKalman.cs
@@ -0,0 +1,33 @@
+namespace CompassVO.Utils.Filters
+{
+ public class SimpleKalman
+ {
+ private double Q = 0.0003;//0.000001;
+ private double R = 0.01;//0.01;
+ private double P = 1, X = 0, K = 0;
+
+ private void measurementUpdate()
+ {
+ K = (P + Q) / (P + Q + R);
+ P = R * (P + Q) / (R + P + Q);
+ }
+
+ public void init(double initX)
+ {
+ X = initX;
+ }
+
+ public double update(double measurement)
+ {
+ measurementUpdate();
+ double result = X + (measurement - X) * K;
+ X = result;
+ return result;
+ }
+
+ public double current()
+ {
+ return X;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Utils/IsolatedStorageHelper.cs b/CompassVO/Utils/IsolatedStorageHelper.cs
new file mode 100644
index 0000000..14efa9c
--- /dev/null
+++ b/CompassVO/Utils/IsolatedStorageHelper.cs
@@ -0,0 +1,105 @@
+using Microsoft.Phone;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows.Media.Imaging;
+
+namespace CompassVO.Utils
+{
+ public class IsolatedStorageHelper
+ {
+ public static void SaveToLocalStorage(string imageFileName, string imageFolder, byte[] content)
+ {
+ if (content == null)
+ {
+ return;
+ }
+
+ var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+ if (!isoFile.DirectoryExists(imageFolder))
+ {
+ isoFile.CreateDirectory(imageFolder);
+ }
+
+ string filePath = Path.Combine(imageFolder, imageFileName);
+ using (var stream = isoFile.CreateFile(filePath))
+ {
+ stream.Write(content, 0, content.Length);
+ }
+ }
+
+ public static WriteableBitmap LoadFromLocalStorage(string imageFileName, string imageFolder)
+ {
+ var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+ if (!isoFile.DirectoryExists(imageFolder))
+ {
+ isoFile.CreateDirectory(imageFolder);
+ }
+ string filePath = Path.Combine(imageFolder, imageFileName);
+ if (!isoFile.FileExists(filePath))
+ {
+ return null;
+ }
+ using (var imageStream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
+ {
+ var imageSource = PictureDecoder.DecodeJpeg(imageStream);
+ return imageSource;
+ }
+ }
+
+ public static byte[] LoadFromLocalStorageArray(string imageFileName, string imageFolder)
+ {
+ var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+ if (!isoFile.DirectoryExists(imageFolder))
+ {
+ isoFile.CreateDirectory(imageFolder);
+ }
+ string filePath = Path.Combine(imageFolder, imageFileName);
+ if (!isoFile.FileExists(filePath))
+ {
+ return null;
+ }
+ using (var imageStream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
+ {
+ byte[] buffer = new byte[imageStream.Length];
+ imageStream.Read(buffer, 0, buffer.Length);
+ return buffer;
+ }
+ }
+
+ public static void CopyFile(string fileSource, string fileDestination)
+ {
+ var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+ if (!isoFile.FileExists(fileSource))
+ {
+ return;
+ }
+ using (var streamSource = isoFile.OpenFile(fileSource, FileMode.Open, FileAccess.Read))
+ {
+ using (var streamDestination = isoFile.CreateFile(fileDestination))
+ {
+ byte[] buffer = new byte[4096];
+ int len = 0;
+ while ((len = streamSource.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ streamDestination.Write(buffer, 0, len);
+ }
+ }
+ }
+ }
+
+ public static void DeleteFile(string fileSource)
+ {
+ var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+ if (isoFile.FileExists(fileSource))
+ {
+ isoFile.DeleteFile(fileSource);
+ }
+ }
+
+ public static bool FileExist(string fileSource)
+ {
+ var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+ return isoFile.FileExists(fileSource);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Utils/Logging/ExceptionPrompt.cs b/CompassVO/Utils/Logging/ExceptionPrompt.cs
new file mode 100644
index 0000000..5751580
--- /dev/null
+++ b/CompassVO/Utils/Logging/ExceptionPrompt.cs
@@ -0,0 +1,73 @@
+using Coding4Fun.Phone.Controls;
+using System;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace CompassVO.Utils.Logging
+{
+ public class ExceptionPrompt : PopUp
+ {
+ 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; }
+
+ private 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 = "Compass VO - Error Report", To = To };
+
+ if (submitCheckBox.IsChecked == true)
+ {
+ task.Show();
+ }
+
+ OnCompleted(new PopUpEventArgs { PopUpResult = PopUpResult.Ok });
+ }
+
+ public void Show(Exception exception)
+ {
+ App.CanExit = false;
+ this.exception = exception;
+ base.Show();
+ }
+
+ public override void OnCompleted(PopUpEventArgs result)
+ {
+ App.CanExit = true;
+ base.OnCompleted(result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Utils/MediaHelper.cs b/CompassVO/Utils/MediaHelper.cs
new file mode 100644
index 0000000..df4bf6d
--- /dev/null
+++ b/CompassVO/Utils/MediaHelper.cs
@@ -0,0 +1,18 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+
+namespace CompassVO.Utils
+{
+ public class MediaHelper
+ {
+ public static void PlaySound(string soundFile)
+ {
+ using (var stream = TitleContainer.OpenStream(soundFile))
+ {
+ var effect = SoundEffect.FromStream(stream);
+ FrameworkDispatcher.Update();
+ effect.Play();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Utils/PhoneThemeDetect.cs b/CompassVO/Utils/PhoneThemeDetect.cs
new file mode 100644
index 0000000..a545bfa
--- /dev/null
+++ b/CompassVO/Utils/PhoneThemeDetect.cs
@@ -0,0 +1,33 @@
+using System.Windows;
+using System.Windows.Media;
+
+namespace CompassVO.Utils
+{
+ public class PhoneThemeDetect
+ {
+ public static Color LightThemeBackground = Color.FromArgb(255, 255, 255, 255);
+ public static Color DarkThemeBackground = Color.FromArgb(255, 0, 0, 0);
+
+ public static bool IsDarkTheme()
+ {
+ SolidColorBrush backgroundBrush = Application.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush;
+ return (backgroundBrush.Color == DarkThemeBackground);
+ }
+
+ public static bool IsLightTheme()
+ {
+ SolidColorBrush backgroundBrush = Application.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush;
+ return (backgroundBrush.Color == LightThemeBackground);
+ }
+
+ public static Color AccentColor()
+ {
+ return (Color)Application.Current.Resources["PhoneAccentColor"];
+ }
+
+ public static SolidColorBrush AccentBrush()
+ {
+ return Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/ViewModel/MainViewModel.cs b/CompassVO/ViewModel/MainViewModel.cs
new file mode 100644
index 0000000..c52ab3a
--- /dev/null
+++ b/CompassVO/ViewModel/MainViewModel.cs
@@ -0,0 +1,1096 @@
+using CompassVO.Model;
+using CompassVO.Model.Messages;
+using CompassVO.Service;
+using CompassVO.Themes;
+using CompassVO.Utils;
+using GalaSoft.MvvmLight.Command;
+using GalaSoft.MvvmLight.Messaging;
+using GalaSoft.MvvmLight.Threading;
+using Microsoft.Devices;
+using Microsoft.Devices.Sensors;
+using Microsoft.Phone.Tasks;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Environment = System.Environment;
+
+namespace CompassVO.ViewModel
+{
+ public class MainViewModel : ViewModelBase
+ {
+ private CompassEx _compass;
+
+ private double initDialAngle = 0;
+ private double initDialAngleDrag = 0;
+ private double startDialAngle = 0;
+ private double deltaDialAngle = 0;
+
+ #region Public Properties
+
+ public const string IsHeadingMagneticPropertyName = "IsHeadingMagnetic";
+
+ public bool IsHeadingMagnetic
+ {
+ get
+ {
+ return AppSettings.Instance.IsHeadingMagnetic;
+ }
+
+ set
+ {
+ if (AppSettings.Instance.IsHeadingMagnetic == value)
+ {
+ return;
+ }
+
+ AppSettings.Instance.IsHeadingMagnetic = value;
+ AppSettings.Save();
+ RaisePropertyChanged(() => IsHeadingMagnetic);
+ }
+ }
+
+ public const string MagneticHeadingPropertyName = "MagneticHeading";
+ private double _magneticheading = 0d;
+
+ public double MagneticHeading
+ {
+ get
+ {
+ return _magneticheading;
+ }
+
+ set
+ {
+ if (_magneticheading == value)
+ return;
+
+ _magneticheading = value;
+ RaisePropertyChanged(() => MagneticHeading);
+ }
+ }
+
+ public const string TrueHeadingPropertyName = "TrueHeading";
+ private double _trueHeading = 0d;
+
+ public double TrueHeading
+ {
+ get
+ {
+ return _trueHeading;
+ }
+
+ set
+ {
+ if (_trueHeading == value)
+ return;
+
+ _trueHeading = value;
+ RaisePropertyChanged(() => TrueHeading);
+ }
+ }
+
+ public const string MagneticDeclinationPropertyName = "MagneticDeclination";
+ private double _magneticDeclination = 0d;
+
+ public double MagneticDeclination
+ {
+ get
+ {
+ return _magneticDeclination;
+ }
+
+ set
+ {
+ if (_magneticDeclination == value)
+ return;
+
+ _magneticDeclination = value;
+ RaisePropertyChanged(() => MagneticDeclination);
+ }
+ }
+
+ public const string HeadingAccuracyPropertyName = "HeadingAccuracy";
+ private double _headingAccuracy = 0d;
+
+ public double HeadingAccuracy
+ {
+ get
+ {
+ return _headingAccuracy;
+ }
+
+ set
+ {
+ if (_headingAccuracy == value)
+ return;
+
+ _headingAccuracy = value;
+ RaisePropertyChanged(() => HeadingAccuracy);
+ }
+ }
+
+ public const string NeedleAnglePropertyName = "NeedleAngle";
+ private double _needleAngle = 0d;
+
+ public double NeedleAngle
+ {
+ get
+ {
+ return _needleAngle;
+ }
+
+ set
+ {
+ if (_needleAngle == value)
+ return;
+
+ _needleAngle = value;
+ RaisePropertyChanged(() => NeedleAngle);
+ Messenger.Default.Send(new UpdateCompassUIMessage(-_needleAngle));
+ }
+ }
+
+ public const string NeedleCardinalDirectionPropertyName = "MagneticCardinalDirection";
+ private CardinalDirection _needleCardinalDirection = CardinalDirection.Unknow;
+
+ public CardinalDirection NeedleCardinalDirection
+ {
+ get
+ {
+ return _needleCardinalDirection;
+ }
+
+ set
+ {
+ if (_needleCardinalDirection == value)
+ return;
+
+ _needleCardinalDirection = value;
+ RaisePropertyChanged(() => NeedleCardinalDirection);
+ }
+ }
+
+ public const string DialAnglePropertyName = "DialAngle";
+ private double _dialAngle = 0d;
+
+ public double DialAngle
+ {
+ get
+ {
+ return _dialAngle;
+ }
+
+ set
+ {
+ if (_dialAngle == value)
+ return;
+ _dialAngle = (value + 360) % 360;
+ RaisePropertyChanged(() => DialAngle);
+ RaisePropertyChanged(() => DialAngleHeading);
+ }
+ }
+
+ public double DialAngleHeading
+ {
+ get
+ {
+ return ((360 - _dialAngle) + 360) % 360;
+ }
+ }
+
+ public const string PhotoAnglePropertyName = "PhotoAngle";
+ private double _photoAngle = 90d;
+
+ public double PhotoAngle
+ {
+ get
+ {
+ return _photoAngle;
+ }
+
+ set
+ {
+ if (_photoAngle == value)
+ return;
+
+ _photoAngle = value % 360;
+ RaisePropertyChanged(() => PhotoAngle);
+ }
+ }
+
+ public const string PhotoScalePropertyName = "PhotoScale";
+ private double _photoScale = 1d;
+
+ public double PhotoScale
+ {
+ get
+ {
+ return _photoScale;
+ }
+
+ set
+ {
+ if (_photoScale == value)
+ return;
+
+ _photoScale = value;
+ RaisePropertyChanged(() => PhotoScale);
+ }
+ }
+
+ public const string PhotoOffsetXPropertyName = "PhotoOffsetX";
+ private double _photoOffsetX = 0d;
+
+ public double PhotoOffsetX
+ {
+ get
+ {
+ return _photoOffsetX;
+ }
+
+ set
+ {
+ if (_photoOffsetX == value)
+ return;
+
+ _photoOffsetX = value;
+ RaisePropertyChanged(() => PhotoOffsetX);
+ }
+ }
+
+ public const string PhotoOffsetYPropertyName = "PhotoOffsetY";
+ private double _photoOffsetY = 0d;
+
+ public double PhotoOffsetY
+ {
+ get
+ {
+ return _photoOffsetY;
+ }
+
+ set
+ {
+ if (_photoOffsetY == value)
+ return;
+
+ _photoOffsetY = value;
+ RaisePropertyChanged(() => PhotoOffsetY);
+ }
+ }
+
+ public const string MagneticCardinalDirectionPropertyName = "MagneticCardinalDirection";
+ private CardinalDirection _magneticCardinalDirection = CardinalDirection.Unknow;
+
+ public CardinalDirection MagneticCardinalDirection
+ {
+ get
+ {
+ return _magneticCardinalDirection;
+ }
+
+ set
+ {
+ if (_magneticCardinalDirection == value)
+ return;
+
+ _magneticCardinalDirection = value;
+ RaisePropertyChanged(() => MagneticCardinalDirection);
+ }
+ }
+
+ public const string TrueCardinalDirectionPropertyName = "TrueCardinalDirection";
+ private CardinalDirection _trueCardinalDirection = CardinalDirection.Unknow;
+
+ public CardinalDirection TrueCardinalDirection
+ {
+ get
+ {
+ return _trueCardinalDirection;
+ }
+
+ set
+ {
+ if (_trueCardinalDirection == value)
+ return;
+
+ _trueCardinalDirection = value;
+ RaisePropertyChanged(() => TrueCardinalDirection);
+ }
+ }
+
+ public const string NeedsCalibrationPropertyName = "NeedsCalibration";
+ private bool _needsCalibration = false;
+
+ public bool NeedsCalibration
+ {
+ get
+ {
+ return _needsCalibration;
+ }
+
+ set
+ {
+ if (_needsCalibration == value)
+ return;
+
+ _needsCalibration = value;
+ RaisePropertyChanged(() => NeedsCalibration);
+ }
+ }
+
+ public const string IsCameraActivePropertyName = "IsCameraActive";
+ private bool _isCameraActive = false;
+
+ public bool IsCameraActive
+ {
+ get
+ {
+ return _isCameraActive;
+ }
+
+ set
+ {
+ if (_isCameraActive == value)
+ return;
+
+ _isCameraActive = value;
+ RaisePropertyChanged(() => IsCameraActive);
+ Messenger.Default.Send(new ShowCameraPreviewMessage(_isCameraActive));
+ }
+ }
+
+ public const string IsDialEditPropertyName = "IsDialEdit";
+ private bool _isDialEdit = false;
+
+ public bool IsDialEdit
+ {
+ get
+ {
+ return _isDialEdit;
+ }
+
+ set
+ {
+ if (_isDialEdit == value)
+ return;
+
+ _isDialEdit = value;
+ RaisePropertyChanged(() => IsDialEdit);
+ }
+ }
+
+ private bool _isDialEditManipulation = false;
+
+ public bool IsDialEditManipulation
+ {
+ get
+ {
+ return _isDialEditManipulation;
+ }
+
+ set
+ {
+ if (_isDialEditManipulation == value)
+ return;
+
+ _isDialEditManipulation = value;
+ RaisePropertyChanged(() => IsDialEditManipulation);
+ }
+ }
+
+ public const string IsBackgroundImageEditPropertyName = "IsBackgroundImageEdit";
+ private bool _isBackgroundImageEdit = false;
+
+ public bool IsBackgroundImageEdit
+ {
+ get
+ {
+ return _isBackgroundImageEdit;
+ }
+
+ set
+ {
+ if (_isBackgroundImageEdit == value)
+ return;
+
+ _isBackgroundImageEdit = value;
+ CompasUIOpacity = value ? 0.4 : 1;
+ RaisePropertyChanged(() => IsBackgroundImageEdit);
+ RaisePropertyChanged(() => HasPhotoAndEditImage);
+ }
+ }
+
+ public const string IsInitCameraPropertyName = "IsInitCamera";
+ private bool _isInitCamera = false;
+
+ public bool IsInitCamera
+ {
+ get
+ {
+ return _isInitCamera;
+ }
+
+ set
+ {
+ if (_isInitCamera == value)
+ return;
+
+ _isInitCamera = value;
+ RaisePropertyChanged(() => IsInitCamera);
+ }
+ }
+
+ public const string IsCompassModePropertyName = "IsCompassMode";
+ private bool _isCompassMode = true;
+
+ public bool IsCompassMode
+ {
+ get
+ {
+ return _isCompassMode;
+ }
+
+ set
+ {
+ if (_isCompassMode == value)
+ return;
+
+ _isCompassMode = value;
+ RaisePropertyChanged(() => IsCompassMode);
+ }
+ }
+
+ public const string HasPhotoPropertyName = "HasPhoto";
+ private bool _hasPhoto = false;
+
+ public bool HasPhoto
+ {
+ get
+ {
+ return _hasPhoto;
+ }
+
+ set
+ {
+ if (_hasPhoto == value)
+ return;
+
+ _hasPhoto = value;
+ RaisePropertyChanged(() => HasPhoto);
+ RaisePropertyChanged(() => HasPhotoAndEditImage);
+ }
+ }
+
+ public List _themes = new List();
+
+ public List Themes
+ {
+ get
+ {
+ return _themes;
+ }
+ set
+ {
+ _themes = value;
+ RaisePropertyChanged(() => Themes);
+ }
+ }
+
+ public const string CurrentThemePropertyName = "CurrentTheme";
+ private ICompassTheme _currentTheme = null;
+
+ public ICompassTheme CurrentTheme
+ {
+ get
+ {
+ return _currentTheme;
+ }
+
+ set
+ {
+ if (_currentTheme == value)
+ return;
+
+ _currentTheme = value;
+ RaisePropertyChanged(() => CurrentTheme);
+ CurrentThemeForegroundColor = new SolidColorBrush(_currentTheme.ForegroundColor);
+
+ if (isLoaded)
+ Messenger.Default.Send(new CompassThemeChangedMessage());
+ }
+ }
+
+ public const string CurrentThemeForegroundColorPropertyName = "CurrentThemeForegroundColor";
+ private SolidColorBrush _currentThemeForegroundColor;
+
+ public SolidColorBrush CurrentThemeForegroundColor
+ {
+ get
+ {
+ return _currentThemeForegroundColor;
+ }
+
+ set
+ {
+ if (_currentThemeForegroundColor == value)
+ return;
+
+ _currentThemeForegroundColor = value;
+ RaisePropertyChanged(() => CurrentThemeForegroundColor);
+ }
+ }
+
+ public const string OperationModePropertyName = "OperationMode";
+ private OperationModeEnum _operationModeEnum = OperationModeEnum.CompassMode;
+
+ public OperationModeEnum OperationMode
+ {
+ get
+ {
+ return _operationModeEnum;
+ }
+
+ set
+ {
+ if (_operationModeEnum == value)
+ return;
+
+ _operationModeEnum = value;
+ RaisePropertyChanged(() => OperationMode);
+ }
+ }
+
+ public bool IsLoading
+ {
+ get
+ {
+ return !isLoaded;
+ }
+ }
+
+ public const string CompasUIOpacityPropertyName = "CompasUIOpacity";
+ private double _compassUiOpacity = 1d;
+
+ public double CompasUIOpacity
+ {
+ get
+ {
+ return _compassUiOpacity;
+ }
+
+ set
+ {
+ if (_compassUiOpacity == value)
+ return;
+
+ _compassUiOpacity = value;
+ RaisePropertyChanged(() => CompasUIOpacity);
+ }
+ }
+
+ public bool HasPhotoAndEditImage
+ {
+ get
+ {
+ return HasPhoto && IsBackgroundImageEdit;
+ }
+ }
+
+ private bool _isSavingPhoto;
+
+ public bool IsSavingPhoto
+ {
+ get
+ {
+ return _isSavingPhoto;
+ }
+ set
+ {
+ if (_isSavingPhoto == value)
+ return;
+ _isSavingPhoto = value;
+ RaisePropertyChanged(() => IsSavingPhoto);
+ }
+ }
+
+ #endregion Public Properties
+
+ #region public Commands
+
+ public RelayCommand BackPictureEdit { get; set; }
+ public RelayCommand CompassDialEdit { get; set; }
+ public RelayCommand ShowSettings { get; set; }
+ public RelayCommand ShowAbout { get; set; }
+
+ public RelayCommand MainPageLoaded { get; set; }
+ public RelayCommand AcceptDialEdit { get; set; }
+ public RelayCommand SendFeedbackCommand { get; set; }
+ public RelayCommand RateMeCommand { get; set; }
+ public RelayCommand OpenWebPageCommand { get; set; }
+ public RelayCommand OpenWebPageVOCommand { get; set; }
+
+ public RelayCommand ThemeSelectedCommand { get; set; }
+
+ public RelayCommand TakePhotoCommand { get; set; }
+ public RelayCommand SelectPhotoCommand { get; set; }
+ public RelayCommand RemovePhotoCommand { get; set; }
+ public RelayCommand AcceptPhotoEdit { get; set; }
+
+ #endregion public Commands
+
+ public MainViewModel()
+ {
+ if (IsInDesignMode)
+ {
+ // Code runs in Blend --> create design time data.
+ InitCompassUI();
+
+ MagneticHeading = 48;
+ TrueHeading = 43;
+ MagneticDeclination = 5;
+ HeadingAccuracy = 10;
+ NeedleAngle = -30;
+ DialAngle = 20;
+ }
+ else
+ {
+ // Code runs "for real"
+ if (Compass.IsSupported)
+ {
+ _compass = new CompassEx();
+ _compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(100);
+ _compass.CurrentValueChanged += new EventHandler>(_compass_CurrentValueChanged);
+ _compass.Calibrate += new EventHandler(_compass_Calibrate);
+ _compass.Calibrated += new EventHandler(_compass_Calibrated);
+ }
+
+ //init commands
+ BackPictureEdit = new RelayCommand(() => BackPictureEditAction());
+ CompassDialEdit = new RelayCommand(() => CompassDialEditAction());
+ ShowSettings = new RelayCommand(() => ShowSettingsAction());
+ ShowAbout = new RelayCommand(() => ShowAboutAction());
+ MainPageLoaded = new RelayCommand(() => MainPageLoadedAction());
+ AcceptDialEdit = new RelayCommand(() => AcceptDialEditAction());
+ SendFeedbackCommand = new RelayCommand(() => SendFeedbackAction());
+ RateMeCommand = new RelayCommand(() => RateMeAction());
+ OpenWebPageCommand = new RelayCommand(() => OpenWebPageAction());
+ OpenWebPageVOCommand = new RelayCommand(() => OpenWebPageVOAction());
+ ThemeSelectedCommand = new RelayCommand((res) => ThemeSelectedAction(res));
+ TakePhotoCommand = new RelayCommand(() => TakePhotoAction());
+ SelectPhotoCommand = new RelayCommand(() => SelectPhotoAction());
+ RemovePhotoCommand = new RelayCommand(() => RemovePhotoAction());
+ AcceptPhotoEdit = new RelayCommand(() => AcceptPhotoEditAction());
+ }
+ }
+
+ private void OpenWebPageVOAction()
+ {
+ WebBrowserTask webBrowserTask = new WebBrowserTask();
+ webBrowserTask.Uri = new Uri(Constants.WEB_PAGE_VO);
+ webBrowserTask.Show();
+ }
+
+ private void InitCompassUI()
+ {
+ if (!IsInDesignMode)
+ {
+ Themes = CompassThemeController.Instance.GetAllThemes();
+ if (!string.IsNullOrEmpty(AppSettings.Instance.CurrentTheme))
+ {
+ ICompassTheme theme = Themes.SingleOrDefault(t => t.ThemeName == AppSettings.Instance.CurrentTheme);
+ if (theme != null)
+ CurrentTheme = theme;
+ else
+ CurrentTheme = Themes[0];
+ }
+ else
+ {
+ CurrentTheme = Themes[0];
+ }
+
+ DialAngle = AppSettings.Instance.DialAngle;
+ }
+ }
+
+ private void AcceptPhotoEditAction()
+ {
+ //save image
+ string fileSource = Path.Combine(Constants.PHOTO_FOLDER, Constants.TEMP_PHOTO_FILENAME);
+ if (IsolatedStorageHelper.FileExist(fileSource))
+ {
+ //save the temp
+ string fileDestination = Path.Combine(Constants.PHOTO_FOLDER, Constants.FINAL_PHOTO_FILENAME);
+ IsolatedStorageHelper.CopyFile(fileSource, fileDestination);
+ IsolatedStorageHelper.DeleteFile(fileSource);
+ }
+ else
+ {
+ //load rotation and scaling values
+ AppSettings.Instance.PhotoOffsetX = PhotoOffsetX;
+ AppSettings.Instance.PhotoOffsetY = PhotoOffsetY;
+ AppSettings.Instance.PhotoScale = PhotoScale;
+ AppSettings.Instance.PhotoAngle = PhotoAngle;
+ AppSettings.Save();
+ }
+ //
+ SetOperationMode(OperationModeEnum.CompassMode);
+ }
+
+ private void RemovePhotoAction()
+ {
+ //remove photo
+ if (MessageBox.Show("Remove background map/image?", "Confirmation", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
+ {
+ string fileDestination = Path.Combine(Constants.PHOTO_FOLDER, Constants.FINAL_PHOTO_FILENAME);
+ IsolatedStorageHelper.DeleteFile(fileDestination);
+ HasPhoto = false;
+ SetOperationMode(OperationModeEnum.CompassMode);
+ }
+ }
+
+ private bool isPhotoChooser;
+
+ private void SelectPhotoAction()
+ {
+ if (!isPhotoChooser)
+ {
+ isPhotoChooser = true;
+
+ //select photo
+ try
+ {
+ Messenger.Default.Send(new ShowCameraPreviewMessage(false));
+ //
+ PhotoChooserTask photoChooserTask = new PhotoChooserTask();
+ photoChooserTask.Completed += new EventHandler(photoChooserTask_Completed);
+ photoChooserTask.Show();
+ }
+ catch (Exception)
+ {
+ }
+ finally
+ {
+ isPhotoChooser = false;
+ }
+ }
+ }
+
+ private void photoChooserTask_Completed(object sender, PhotoResult e)
+ {
+ isPhotoChooser = false;
+ if (e.ChosenPhoto != null && e.ChosenPhoto.Length > 0)
+ {
+ using (MemoryStream ms = new MemoryStream())
+ {
+ byte[] buffer = new byte[4096];
+ int len = 0;
+ while ((len = e.ChosenPhoto.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ ms.Write(buffer, 0, len);
+ }
+ IsolatedStorageHelper.SaveToLocalStorage(Constants.FINAL_PHOTO_FILENAME, Constants.PHOTO_FOLDER, ms.ToArray());
+ }
+ Messenger.Default.Send(new ShowCameraPreviewMessage(false));
+
+ WriteableBitmap bmp = IsolatedStorageHelper.LoadFromLocalStorage(Constants.FINAL_PHOTO_FILENAME, Constants.PHOTO_FOLDER);
+ App.Locator.Main.PhotoOffsetX = -(double)bmp.PixelWidth / 2;
+ App.Locator.Main.PhotoOffsetY = -(double)bmp.PixelHeight / 2;
+ App.Locator.Main.PhotoScale = (double)1066 / bmp.PixelWidth;
+ App.Locator.Main.PhotoAngle = 0;
+ App.Locator.Main.SavePhotoSettings();
+
+ App.Locator.Main.HasPhoto = true;
+ Messenger.Default.Send(new LoadPhotoMessage());
+ Messenger.Default.Send(new RefreshAppBarMessage());
+ }
+ else
+ {
+ Messenger.Default.Send(new ShowCameraPreviewMessage(true));
+ }
+ }
+
+ private void TakePhotoAction()
+ {
+ //take photo
+ Messenger.Default.Send(new TakePhotoMessage());
+ }
+
+ private void ThemeSelectedAction(ICompassTheme res)
+ {
+ CurrentTheme = res;
+ //save
+ AppSettings.Instance.CurrentTheme = res.ThemeName;
+ AppSettings.Save();
+ //
+ App.Locator.NavigationService.GoBack();
+ }
+
+ private void OpenWebPageAction()
+ {
+ WebBrowserTask webBrowserTask = new WebBrowserTask();
+ webBrowserTask.Uri = new Uri(Constants.WEB_PAGE);
+ webBrowserTask.Show();
+ }
+
+ private void SendFeedbackAction()
+ {
+ EmailComposeTask emailTask = new EmailComposeTask();
+ emailTask.Subject = "Compass VO - Feedback and ideas";
+ emailTask.To = Constants.JWP_SUPPORT_EMAIL;
+ emailTask.Body = "Hi Just Windows Phone!" + Environment.NewLine + Environment.NewLine + "Hereby my feedback:" + Environment.NewLine + "- " + Environment.NewLine + Environment.NewLine + "See ya!";
+ emailTask.Show();
+ }
+
+ private void RateMeAction()
+ {
+ try
+ {
+ MarketplaceReviewTask marketplaceReviewTask = new MarketplaceReviewTask();
+ marketplaceReviewTask.Show();
+ }
+ catch (Exception)
+ {
+ MessageBox.Show("The application cannot be reviewed!");
+ }
+ }
+
+ private void AcceptDialEditAction()
+ {
+ //save
+ AppSettings.Instance.DialAngle = DialAngle;
+ AppSettings.Save();
+ //
+ IsDialEdit = false;
+ IsCompassMode = true;
+ Messenger.Default.Send(new RefreshAppBarMessage());
+ }
+
+ private void ShowAboutAction()
+ {
+ App.Locator.NavigationService.NavigateTo(new Uri("/AboutPage.xaml", UriKind.Relative));
+ }
+
+ private bool isLoaded;
+
+ private void MainPageLoadedAction()
+ {
+ LoadPhoto();
+
+ isLoaded = true;
+ RaisePropertyChanged(() => IsLoading);
+
+ //init UI
+ InitCompassUI();
+
+ if (Microsoft.Devices.Environment.DeviceType == DeviceType.Emulator)
+ {
+ MagneticHeading = 55;
+ TrueHeading = 50;
+ MagneticDeclination = 5;
+ HeadingAccuracy = 10;
+ NeedleAngle = 55;
+ NeedleCardinalDirection = CardinalDirection.NE;
+ DialAngle = 0;
+ MagneticCardinalDirection = CardinalDirection.NW;
+ TrueCardinalDirection = CardinalDirection.NW;
+ PhotoAngle = -15;
+ }
+
+ Messenger.Default.Send(new RefreshAppBarMessage());
+
+ if (Compass.IsSupported && _compass != null)
+ {
+ _compass.Start();
+ }
+ else
+ {
+ MessageBox.Show("Your device doesn't have compass sensor!");
+ }
+ }
+
+ private void LoadPhoto()
+ {
+ LoadPhotoSettings();
+
+ WriteableBitmap bmp = IsolatedStorageHelper.LoadFromLocalStorage(Constants.FINAL_PHOTO_FILENAME, Constants.PHOTO_FOLDER);
+ HasPhoto = bmp != null;
+
+ Messenger.Default.Send(new LoadPhotoMessage());
+ }
+
+ public void LoadPhotoSettings()
+ {
+ PhotoOffsetX = AppSettings.Instance.PhotoOffsetX;
+ PhotoOffsetY = AppSettings.Instance.PhotoOffsetY;
+ PhotoScale = AppSettings.Instance.PhotoScale;
+ PhotoAngle = AppSettings.Instance.PhotoAngle;
+ }
+
+ public void SavePhotoSettings()
+ {
+ AppSettings.Instance.PhotoOffsetX = PhotoOffsetX;
+ AppSettings.Instance.PhotoOffsetY = PhotoOffsetY;
+ AppSettings.Instance.PhotoScale = PhotoScale;
+ AppSettings.Instance.PhotoAngle = PhotoAngle;
+ AppSettings.Save();
+ }
+
+ private void ShowSettingsAction()
+ {
+ App.Locator.NavigationService.NavigateTo(new Uri("/SettingsPage.xaml", UriKind.Relative));
+ }
+
+ private double initPhotoOffsetX;
+ private double initPhotoOffsetY;
+ private double initPhotoScale;
+ private double initPhotoAngle;
+
+ private void BackPictureEditAction()
+ {
+ if (CurrentTheme.SupportsBackgroundImages)
+ {
+ if (!HasPhoto)
+ {
+ IsInitCamera = true;
+ IsCameraActive = true;
+ }
+ else
+ {
+ //store init values
+ initPhotoOffsetX = PhotoOffsetX;
+ initPhotoOffsetY = PhotoOffsetY;
+ initPhotoScale = PhotoScale;
+ initPhotoAngle = PhotoAngle;
+ }
+ SetOperationMode(OperationModeEnum.PhotoEdit);
+ }
+ }
+
+ private void CompassDialEditAction()
+ {
+ if (CurrentTheme.SupportsDialRotation)
+ {
+ initDialAngle = DialAngle;
+ SetOperationMode(OperationModeEnum.DialEdit);
+ }
+ }
+
+ private void _compass_CurrentValueChanged(object sender, SensorReadingEventArgs e)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(() =>
+ {
+ MagneticHeading = e.SensorReading.MagneticHeading;
+ TrueHeading = e.SensorReading.TrueHeading;
+ MagneticDeclination = e.SensorReading.MagneticDeclination;
+ HeadingAccuracy = e.SensorReading.HeadingAccuracy;
+ NeedleAngle = IsHeadingMagnetic ? e.SensorReading.MagneticHeading : e.SensorReading.TrueHeading;
+ NeedleCardinalDirection = IsHeadingMagnetic ? e.SensorReading.MagneticCardinalDirection : e.SensorReading.TrueCardinalDirection;
+ MagneticCardinalDirection = e.SensorReading.MagneticCardinalDirection;
+ TrueCardinalDirection = e.SensorReading.TrueCardinalDirection;
+ });
+ }
+
+ private void _compass_Calibrated(object sender, CalibrationEventArgs e)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(() =>
+ {
+ NeedsCalibration = false;
+ Microsoft.Devices.VibrateController.Default.Start(TimeSpan.FromMilliseconds(300));
+ });
+ }
+
+ private void _compass_Calibrate(object sender, CalibrationEventArgs e)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(() =>
+ {
+ NeedsCalibration = true;
+ Microsoft.Devices.VibrateController.Default.Start(TimeSpan.FromMilliseconds(300));
+ });
+ }
+
+ public void CompassDialEditStarted(Point point)
+ {
+ if (IsDialEdit)
+ {
+ IsDialEditManipulation = true;
+ initDialAngleDrag = DialAngle;
+ startDialAngle = CurrentTheme.GetAngle(point);
+ }
+ }
+
+ public void CompassDialEditCompleted()
+ {
+ if (IsDialEdit)
+ {
+ IsDialEditManipulation = false;
+ }
+ }
+
+ public void CompassDialEditDelta(Point point)
+ {
+ if (IsDialEdit)
+ {
+ deltaDialAngle = CurrentTheme.GetAngle(point);
+ DialAngle = initDialAngleDrag + (startDialAngle - deltaDialAngle);
+ }
+ }
+
+ private void SetOperationMode(OperationModeEnum operationMode)
+ {
+ OperationMode = operationMode;
+
+ switch (operationMode)
+ {
+ case OperationModeEnum.CompassMode:
+ IsCompassMode = true;
+ IsBackgroundImageEdit = false;
+ IsDialEdit = false;
+ break;
+
+ case OperationModeEnum.DialEdit:
+ IsCompassMode = false;
+ IsBackgroundImageEdit = false;
+ IsDialEdit = true;
+ break;
+
+ case OperationModeEnum.PhotoEdit:
+ IsCompassMode = false;
+ IsBackgroundImageEdit = true;
+ IsDialEdit = false;
+ break;
+ }
+ Messenger.Default.Send(new RefreshAppBarMessage());
+ }
+
+ public void CancelEdit()
+ {
+ if (IsDialEdit)
+ {
+ DialAngle = initDialAngle;
+ }
+ else if (IsBackgroundImageEdit)
+ {
+ IsCameraActive = false;
+ PhotoOffsetX = initPhotoOffsetX;
+ PhotoOffsetY = initPhotoOffsetY;
+ PhotoScale = initPhotoScale;
+ PhotoAngle = initPhotoAngle;
+ }
+
+ SetOperationMode(OperationModeEnum.CompassMode);
+ }
+
+ public override void Cleanup()
+ {
+ if (Compass.IsSupported && _compass != null)
+ {
+ _compass.Stop();
+ }
+ base.Cleanup();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/ViewModel/ViewModelBase.cs b/CompassVO/ViewModel/ViewModelBase.cs
new file mode 100644
index 0000000..2d3dba6
--- /dev/null
+++ b/CompassVO/ViewModel/ViewModelBase.cs
@@ -0,0 +1,36 @@
+using CompassVO.Model;
+using CompassVO.Service.Navigation;
+using System.Runtime.Serialization;
+
+namespace CompassVO.ViewModel
+{
+ [DataContract]
+ public class ViewModelBase : GalaSoft.MvvmLight.ViewModelBase
+ {
+ private object context;
+
+ public object Context
+ {
+ get { return context; }
+ set
+ {
+ if (context == value)
+ return;
+ context = value;
+ RaisePropertyChanged("Context");
+ }
+ }
+
+ ///
+ /// Gets PageNavigation from Container
+ ///
+ public INavigationService NavigationService
+ {
+ get
+ {
+ INavigationService pageNav = (INavigationService)Container.Instance.Resolve(typeof(NavigationService), "NavigationService");
+ return pageNav;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/ViewModel/ViewModelLocator.cs b/CompassVO/ViewModel/ViewModelLocator.cs
new file mode 100644
index 0000000..19e0b6b
--- /dev/null
+++ b/CompassVO/ViewModel/ViewModelLocator.cs
@@ -0,0 +1,77 @@
+/*
+ In App.xaml:
+
+
+
+
+ In the View:
+ DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
+
+ You can also use Blend to do all this with the tool's support.
+ See http://www.galasoft.ch/mvvm
+*/
+
+using CompassVO.Model;
+using CompassVO.Service.Navigation;
+
+namespace CompassVO.ViewModel
+{
+ ///
+ /// This class contains static references to all the view models in the
+ /// application and provides an entry point for the bindings.
+ ///
+ public class ViewModelLocator
+ {
+ private static MainViewModel _main;
+
+ ///
+ /// Initializes a new instance of the ViewModelLocator class.
+ ///
+ public ViewModelLocator()
+ {
+ ////if (ViewModelBase.IsInDesignModeStatic)
+ ////{
+ //// // Create design time services and viewmodels
+ ////}
+ ////else
+ ////{
+ //// // Create run time services and view models
+ ////}
+
+ _main = new MainViewModel();
+ }
+
+ ///
+ /// Gets the Main property which defines the main viewmodel.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This non-static member is needed for data binding purposes.")]
+ public MainViewModel Main
+ {
+ get
+ {
+ return _main;
+ }
+ }
+
+ public void Cleanup()
+ {
+ if (Main != null)
+ Main.Cleanup();
+ }
+
+ ///
+ /// Gets PageNavigation from Container
+ ///
+ public INavigationService NavigationService
+ {
+ get
+ {
+ INavigationService pageNav = (INavigationService)Container.Instance.Resolve(typeof(NavigationService), "NavigationService");
+ return pageNav;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/Views/compass_calibration.xaml b/CompassVO/Views/compass_calibration.xaml
new file mode 100644
index 0000000..a23ec34
--- /dev/null
+++ b/CompassVO/Views/compass_calibration.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ The compass on your device needs to be calibrated.
+ Hold the device in front of you and sweep it through a figure 8 pattern as shown
+ until the calibration is complete.
+
+ heading accuracy:
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/Views/compass_calibration.xaml.cs b/CompassVO/Views/compass_calibration.xaml.cs
new file mode 100644
index 0000000..5843f93
--- /dev/null
+++ b/CompassVO/Views/compass_calibration.xaml.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+
+namespace CompassVO.Views
+{
+ public partial class compass_calibration : UserControl
+ {
+ public compass_calibration()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CompassVO/app.config b/CompassVO/app.config
new file mode 100644
index 0000000..5c4ef1a
--- /dev/null
+++ b/CompassVO/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CompassVO/packages.config b/CompassVO/packages.config
new file mode 100644
index 0000000..79242c2
--- /dev/null
+++ b/CompassVO/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Phone7.Fx.Preview/BindableApplicationBar.xaml.cs b/Phone7.Fx.Preview/BindableApplicationBar.xaml.cs
new file mode 100644
index 0000000..b2438ab
--- /dev/null
+++ b/Phone7.Fx.Preview/BindableApplicationBar.xaml.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Markup;
+using System.Windows.Media;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+
+namespace Phone7.Fx.Preview
+{
+ [ContentProperty("Buttons")]
+ public class BindableApplicationBar : ItemsControl, IApplicationBar
+ {
+ private readonly ApplicationBar _applicationBar;
+
+ public BindableApplicationBar()
+ {
+ _applicationBar = new ApplicationBar();
+ this.Loaded += new RoutedEventHandler(BindableApplicationBar_Loaded);
+ }
+
+ void BindableApplicationBar_Loaded(object sender, RoutedEventArgs e)
+ {
+ var page =
+ this.GetVisualAncestors().Where(c => c is PhoneApplicationPage).FirstOrDefault() as PhoneApplicationPage;
+ if (page != null) page.ApplicationBar = _applicationBar;
+ }
+
+ protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ base.OnItemsChanged(e);
+ _applicationBar.Buttons.Clear();
+ _applicationBar.MenuItems.Clear();
+ foreach (BindableApplicationBarIconButton button in Items.Where(c => c is BindableApplicationBarIconButton))
+ {
+ _applicationBar.Buttons.Add(button.Button);
+ }
+ foreach (BindableApplicationBarMenuItem button in Items.Where(c => c is BindableApplicationBarMenuItem))
+ {
+ _applicationBar.MenuItems.Add(button.MenuItem);
+ }
+ }
+
+ public static readonly DependencyProperty IsVisibleProperty =
+ DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(BindableApplicationBar), new PropertyMetadata(true, OnVisibleChanged));
+
+ private static void OnVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.NewValue != e.OldValue)
+ {
+ ((BindableApplicationBar)d)._applicationBar.IsVisible = (bool)e.NewValue;
+ }
+ }
+
+ public static readonly DependencyProperty IsMenuEnabledProperty =
+ DependencyProperty.RegisterAttached("IsMenuEnabled", typeof(bool), typeof(BindableApplicationBar), new PropertyMetadata(true, OnEnabledChanged));
+
+ private static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.NewValue != e.OldValue)
+ {
+ ((BindableApplicationBar)d)._applicationBar.IsMenuEnabled = (bool)e.NewValue;
+ }
+ }
+
+ public bool IsVisible
+ {
+ get { return (bool)GetValue(IsVisibleProperty); }
+ set { SetValue(IsVisibleProperty, value); }
+ }
+
+ public double BarOpacity
+ {
+ get { return _applicationBar.Opacity; }
+ set { _applicationBar.Opacity = value; }
+ }
+
+ public bool IsMenuEnabled
+ {
+ get { return (bool)GetValue(IsMenuEnabledProperty); }
+ set { SetValue(IsMenuEnabledProperty, value); }
+ }
+
+ public Color BackgroundColor
+ {
+ get { return _applicationBar.BackgroundColor; }
+ set { _applicationBar.BackgroundColor = value; }
+ }
+
+ public Color ForegroundColor
+ {
+ get { return _applicationBar.ForegroundColor; }
+ set { _applicationBar.ForegroundColor = value; }
+ }
+
+ public ApplicationBarMode Mode { get; set; }
+ public double DefaultSize { get; private set; }
+ public double MiniSize { get; private set; }
+
+ public IList Buttons
+ {
+ get { return this.Items; }
+
+ }
+
+ public IList MenuItems
+ {
+ get { return this.Items; }
+ }
+
+ public event EventHandler StateChanged;
+ }
+}
\ No newline at end of file
diff --git a/Phone7.Fx.Preview/BindableApplicationBarIconButton.xaml.cs b/Phone7.Fx.Preview/BindableApplicationBarIconButton.xaml.cs
new file mode 100644
index 0000000..c04cf0b
--- /dev/null
+++ b/Phone7.Fx.Preview/BindableApplicationBarIconButton.xaml.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Windows;
+using System.Windows.Input;
+using Microsoft.Phone.Shell;
+
+namespace Phone7.Fx.Preview
+{
+ public class BindableApplicationBarIconButton : FrameworkElement, IApplicationBarIconButton, IApplicationBarMenuItem
+ {
+
+ public static readonly DependencyProperty CommandProperty =
+ DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(BindableApplicationBarIconButton), null);
+
+ public ICommand Command
+ {
+ get { return (ICommand)GetValue(CommandProperty); }
+ set { SetValue(CommandProperty, value); }
+ }
+
+ public static readonly DependencyProperty CommandParameterProperty =
+ DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(BindableApplicationBarIconButton), null);
+
+ public object CommandParameter
+ {
+ get { return GetValue(CommandParameterProperty); }
+ set { SetValue(CommandParameterProperty, value); }
+ }
+
+
+ public static readonly DependencyProperty CommandParameterValueProperty =
+ DependencyProperty.RegisterAttached("CommandParameterValue", typeof(object), typeof(BindableApplicationBarMenuItem), null);
+
+ public object CommandParameterValue
+ {
+ get { return GetValue(CommandParameterValueProperty); }
+ set { SetValue(CommandParameterValueProperty, value); }
+ }
+
+ public static readonly DependencyProperty IsEnabledProperty =
+ DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(BindableApplicationBarIconButton), new PropertyMetadata(true, OnEnabledChanged));
+
+ private static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.NewValue != e.OldValue)
+ {
+ ((BindableApplicationBarIconButton)d).Button.IsEnabled = (bool)e.NewValue;
+ }
+ }
+
+ public static readonly DependencyProperty TextProperty =
+ DependencyProperty.RegisterAttached("Text", typeof(string), typeof(BindableApplicationBarIconButton), new PropertyMetadata(OnTextChanged));
+
+ private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.NewValue != e.OldValue)
+ {
+ ((BindableApplicationBarIconButton)d).Button.Text = e.NewValue.ToString();
+ }
+ }
+
+ public ApplicationBarIconButton Button { get; set; }
+
+ public BindableApplicationBarIconButton()
+ {
+ Button = new ApplicationBarIconButton();
+ Button.Text = "Text";
+ Button.Click += ApplicationBarIconButtonClick;
+ }
+
+ void ApplicationBarIconButtonClick(object sender, EventArgs e)
+ {
+ if (Command != null && CommandParameter != null)
+ Command.Execute(CommandParameter);
+ else if (Command != null)
+ Command.Execute(CommandParameterValue);
+ }
+
+ public bool IsEnabled
+ {
+ get { return (bool)GetValue(IsEnabledProperty); }
+ set { SetValue(IsEnabledProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public event EventHandler Click;
+
+ public Uri IconUri
+ {
+ get { return Button.IconUri; }
+ set { Button.IconUri = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Phone7.Fx.Preview/BindableApplicationBarMenuItem.xaml.cs b/Phone7.Fx.Preview/BindableApplicationBarMenuItem.xaml.cs
new file mode 100644
index 0000000..db93180
--- /dev/null
+++ b/Phone7.Fx.Preview/BindableApplicationBarMenuItem.xaml.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Windows;
+using System.Windows.Input;
+using Microsoft.Phone.Shell;
+
+namespace Phone7.Fx.Preview
+{
+ public class BindableApplicationBarMenuItem : FrameworkElement, IApplicationBarMenuItem
+ {
+
+ public static readonly DependencyProperty CommandProperty =
+ DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(BindableApplicationBarMenuItem), null);
+
+ public ICommand Command
+ {
+ get { return (ICommand)GetValue(CommandProperty); }
+ set { SetValue(CommandProperty, value); }
+ }
+
+ public static readonly DependencyProperty CommandParameterProperty =
+ DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(BindableApplicationBarMenuItem), null);
+
+ public object CommandParameter
+ {
+ get { return GetValue(CommandParameterProperty); }
+ set { SetValue(CommandParameterProperty, value); }
+ }
+
+
+ public static readonly DependencyProperty CommandParameterValueProperty =
+ DependencyProperty.RegisterAttached("CommandParameterValue", typeof(object), typeof(BindableApplicationBarMenuItem), null);
+
+ public object CommandParameterValue
+ {
+ get { return GetValue(CommandParameterValueProperty); }
+ set { SetValue(CommandParameterValueProperty, value); }
+ }
+
+ public static readonly DependencyProperty IsEnabledProperty =
+ DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(BindableApplicationBarMenuItem), new PropertyMetadata(true, OnEnabledChanged));
+
+ private static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.NewValue != e.OldValue)
+ {
+ ((BindableApplicationBarMenuItem)d).MenuItem.IsEnabled = (bool)e.NewValue;
+ }
+ }
+
+ public static readonly DependencyProperty TextProperty =
+ DependencyProperty.RegisterAttached("Text", typeof(string), typeof(BindableApplicationBarMenuItem), new PropertyMetadata(OnTextChanged));
+
+ private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.NewValue != e.OldValue)
+ {
+ ((BindableApplicationBarMenuItem)d).MenuItem.Text = e.NewValue.ToString();
+ }
+ }
+
+ public ApplicationBarMenuItem MenuItem { get; set; }
+
+ public BindableApplicationBarMenuItem()
+ {
+ MenuItem = new ApplicationBarMenuItem();
+ MenuItem.Text = "Text";
+ MenuItem.Click += ApplicationBarMenuItemClick;
+ }
+
+ void ApplicationBarMenuItemClick(object sender, EventArgs e)
+ {
+ if (Command != null && CommandParameter != null)
+ Command.Execute(CommandParameter);
+ else if (Command != null)
+ Command.Execute(CommandParameterValue);
+ }
+
+ public bool IsEnabled
+ {
+ get { return (bool)GetValue(IsEnabledProperty); }
+ set { SetValue(IsEnabledProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public event EventHandler Click;
+
+
+ }
+}
\ No newline at end of file
diff --git a/Phone7.Fx.Preview/Cache.cs b/Phone7.Fx.Preview/Cache.cs
new file mode 100644
index 0000000..142d830
--- /dev/null
+++ b/Phone7.Fx.Preview/Cache.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Linq;
+using System.Runtime.Serialization;
+
+namespace Phone7.Fx.Preview
+{
+ ///
+ ///
+ ///
+ public class Cache
+ {
+ public static readonly DateTime NoAbsoluteExpiration = DateTime.MaxValue;
+ public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
+
+ readonly IsolatedStorageFile _myStore = IsolatedStorageFile.GetUserStoreForApplication();
+
+ private object _sync = new object();
+
+
+ private static Cache _current;
+ ///
+ /// Gets the current instance of the cache
+ ///
+ /// The current.
+ public static Cache Current
+ {
+ get { return _current ?? (_current = new Cache()); }
+ }
+
+ ///
+ /// Adds the specified key.
+ ///
+ /// The key.
+ /// The value.
+ /// The absolute expiration.
+ /// The sliding expiration.
+ public void Add(string key, object value, DateTime absoluteExpiration, TimeSpan slidingExpiration)
+ {
+ lock (_sync)
+ {
+ if (Contains(key))
+ Remove(key);
+
+ if (absoluteExpiration == NoAbsoluteExpiration)
+ Add(key, DateTime.UtcNow + slidingExpiration, value);
+ if (slidingExpiration == NoSlidingExpiration)
+ Add(key, absoluteExpiration, value);
+ }
+ }
+
+ ///
+ /// Adds the specified key.
+ ///
+ /// The key.
+ /// The expiration date.
+ /// The value.
+ private void Add(string key, DateTime expirationDate, object value)
+ {
+ lock (_sync)
+ {
+ if (!_myStore.DirectoryExists(key))
+ _myStore.CreateDirectory(key);
+ else
+ {
+ string currentFile = GetFileNames(key).FirstOrDefault();
+ if (currentFile != null)
+ _myStore.DeleteFile(string.Format("{0}\\{1}", key, currentFile));
+ _myStore.DeleteDirectory(key);
+ _myStore.CreateDirectory(key);
+ }
+
+ string fileName = string.Format("{0}\\{1}.cache", key, expirationDate.ToFileTimeUtc());
+
+ if (_myStore.FileExists(fileName))
+ _myStore.DeleteFile(fileName);
+
+ NormalWrite(fileName, value);
+ }
+
+ }
+
+ ///
+ /// Determines whether the cache contains the specified key.
+ ///
+ /// The key.
+ ///
+ /// true if [contains] [the specified key]; otherwise, false.
+ ///
+ public bool Contains(string key)
+ {
+ lock (_sync)
+ {
+ if (_myStore.DirectoryExists(key) && GetFileNames(key).Any())
+ {
+ string currentFile = GetFileNames(key).FirstOrDefault();
+ if (currentFile != null)
+ {
+ var expirationDate =
+ DateTime.FromFileTimeUtc(long.Parse(Path.GetFileNameWithoutExtension(currentFile)));
+ if (expirationDate >= DateTime.UtcNow)
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ ///
+ /// Removes the specified key.
+ ///
+ /// The key.
+ public void Remove(string key)
+ {
+ lock (_sync)
+ {
+ if (!Contains(key))
+ throw new AccessViolationException("The key does not exist in the cache");
+ string currentFile = GetFileNames(key).FirstOrDefault();
+ if (currentFile != null)
+ _myStore.DeleteFile(string.Format("{0}\\{1}", key, currentFile));
+ _myStore.DeleteDirectory(key);
+ }
+ }
+
+ ///
+ /// Gets the file names.
+ ///
+ /// The key.
+ ///
+ private IEnumerable GetFileNames(string key)
+ {
+ return _myStore.GetFileNames(string.Format("{0}\\*.cache", key));
+ }
+
+ ///
+ /// Gets the specified key.
+ ///
+ ///
+ /// The key.
+ ///
+ public T Get(string key)
+ {
+ lock (_sync)
+ {
+ string currentFile = GetFileNames(key).FirstOrDefault();
+ if (currentFile != null)
+ {
+ var expirationDate =
+ DateTime.FromFileTimeUtc(long.Parse(Path.GetFileNameWithoutExtension(currentFile)));
+ if (expirationDate >= DateTime.UtcNow)
+ {
+ return NormalRead(string.Format(@"{0}\{1}", key, currentFile));
+ }
+ Remove(key);
+ }
+ return default(T);
+ }
+
+ }
+
+ #region Serialization
+
+ private T NormalRead(string fileName)
+ {
+ using (var isolatedStorageFileStream = new IsolatedStorageFileStream(fileName, FileMode.Open, _myStore))
+ {
+ DataContractSerializer s = new DataContractSerializer(typeof(T));
+
+ var value = s.ReadObject(isolatedStorageFileStream);
+ isolatedStorageFileStream.Close();
+ return (T)value;
+ }
+ }
+
+ private void NormalWrite(string fileName, object value)
+ {
+ using (var isolatedStorageFileStream = new IsolatedStorageFileStream(fileName, FileMode.OpenOrCreate, _myStore))
+ {
+ DataContractSerializer s = new DataContractSerializer(value.GetType());
+
+ s.WriteObject(isolatedStorageFileStream, value);
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Phone7.Fx.Preview/Phone7.Fx.Preview.csproj b/Phone7.Fx.Preview/Phone7.Fx.Preview.csproj
new file mode 100644
index 0000000..1957617
--- /dev/null
+++ b/Phone7.Fx.Preview/Phone7.Fx.Preview.csproj
@@ -0,0 +1,117 @@
+
+
+
+ Debug
+ AnyCPU
+ 10.0.20506
+ 2.0
+ {B55A0F90-2B5A-4C4B-88F4-013AA1629866}
+ {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ Phone7.Fx.Preview
+ Phone7.Fx.Preview
+ v8.0
+
+
+
+
+ WindowsPhone
+ false
+ true
+ true
+
+
+
+
+ 4.0
+ 11.0
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+ true
+ full
+ false
+ Bin\Debug
+ DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE
+ true
+ true
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ Bin\Release
+ TRACE;SILVERLIGHT;WINDOWS_PHONE
+ true
+ true
+ prompt
+ 4
+
+
+
+ Bin\x86\Debug
+ true
+ full
+ false
+
+
+
+ Bin\x86\Release
+ pdbonly
+ true
+
+
+
+ Bin\ARM\Debug
+ true
+ full
+ false
+
+
+
+ Bin\ARM\Release
+ pdbonly
+ true
+
+
+
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Phone7.Fx.Preview/Properties/AssemblyInfo.cs b/Phone7.Fx.Preview/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..479d9e3
--- /dev/null
+++ b/Phone7.Fx.Preview/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Phone7.Fx.Preview")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Phone7.Fx.Preview")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f2bd3027-1a6d-4c83-9434-4ad1349fd608")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Phone7.Fx.Preview/VisualTreeHelperExtensions.cs b/Phone7.Fx.Preview/VisualTreeHelperExtensions.cs
new file mode 100644
index 0000000..e031a96
--- /dev/null
+++ b/Phone7.Fx.Preview/VisualTreeHelperExtensions.cs
@@ -0,0 +1,394 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Phone7.Fx.Preview
+{
+ public static class VisualTreeHelperExtensions
+ {
+ ///
+ /// Equivalent of FindName, but works on the visual tree to go through templates, etc.
+ ///
+ /// The node to search from
+ /// The name to look for
+ /// The found node, or null if not found
+ public static FrameworkElement FindVisualChild(this FrameworkElement root, string name)
+ {
+ FrameworkElement temp = root.FindName(name) as FrameworkElement;
+ if (temp != null)
+ return temp;
+
+ foreach (FrameworkElement element in root.GetVisualChildren())
+ {
+ temp = element.FindName(name) as FrameworkElement;
+ if (temp != null)
+ return temp;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets the visual parent of the element
+ ///
+ /// The element to check
+ /// The visual parent
+ public static FrameworkElement GetVisualParent(this FrameworkElement node)
+ {
+ return VisualTreeHelper.GetParent(node) as FrameworkElement;
+ }
+
+ ///
+ /// Gets a visual child of the element
+ ///
+ /// The element to check
+ /// The index of the child
+ /// The found child
+ public static FrameworkElement GetVisualChild(this FrameworkElement node, int index)
+ {
+ return VisualTreeHelper.GetChild(node, index) as FrameworkElement;
+ }
+
+ ///
+ /// Gets all the visual children of the element
+ ///
+ /// The element to get children of
+ /// An enumerator of the children
+ public static IEnumerable GetVisualChildren(this FrameworkElement root)
+ {
+ for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
+ yield return VisualTreeHelper.GetChild(root, i) as FrameworkElement;
+ }
+
+ ///
+ /// Gets the ancestors of the element, up to the root
+ ///
+ /// The element to start from
+ /// An enumerator of the ancestors
+ public static IEnumerable GetVisualAncestors(this FrameworkElement node)
+ {
+ FrameworkElement parent = node.GetVisualParent();
+ while (parent != null)
+ {
+ yield return parent;
+ parent = parent.GetVisualParent();
+ }
+ }
+
+ ///
+ /// Gets the VisualStateGroup with the given name, looking up the visual tree
+ ///
+ /// Element to start from
+ /// Name of the group to look for
+ /// Whether or not to look up the tree
+ /// The group, if found
+ public static VisualStateGroup GetVisualStateGroup(this FrameworkElement root, string groupName, bool searchAncestors)
+ {
+ IList groups = VisualStateManager.GetVisualStateGroups(root);
+ foreach (object o in groups)
+ {
+ VisualStateGroup group = o as VisualStateGroup;
+ if (group != null && group.Name == groupName)
+ return group;
+ }
+
+ if (searchAncestors)
+ {
+ FrameworkElement parent = root.GetVisualParent();
+ if (parent != null)
+ return parent.GetVisualStateGroup(groupName, true);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Finds the VisualStateGroup with the given name
+ ///
+ /// The root.
+ /// The name.
+ ///
+ public static VisualStateGroup FindVisualState(this FrameworkElement root, string name)
+ {
+ if (root == null)
+ return null;
+
+ IList groups = VisualStateManager.GetVisualStateGroups(root);
+ return groups.Cast().FirstOrDefault(group => group.Name == name);
+ }
+
+ ///
+ /// Performs a breadth-first enumeration of all the descendents in the tree
+ ///
+ /// The root node
+ /// An enumerator of all the children
+ public static IEnumerable GetVisualDescendents(this FrameworkElement root)
+ {
+ Queue> toDo = new Queue>();
+
+ toDo.Enqueue(root.GetVisualChildren());
+ while (toDo.Count > 0)
+ {
+ IEnumerable children = toDo.Dequeue();
+ foreach (FrameworkElement child in children)
+ {
+ yield return child;
+ toDo.Enqueue(child.GetVisualChildren());
+ }
+ }
+ }
+
+ ///
+ /// Provides a debug string that represents the visual child tree
+ ///
+ /// The root node
+ /// StringBuilder into which the text is appended
+ /// This method only works in DEBUG mode
+ [Conditional("DEBUG")]
+ public static void GetVisualChildTreeDebugText(this FrameworkElement root, StringBuilder result)
+ {
+ List results = new List();
+ root.GetChildTree("", " ", results);
+ foreach (string s in results)
+ result.AppendLine(s);
+ }
+
+ private static void GetChildTree(this FrameworkElement root, string prefix, string addPrefix, List results)
+ {
+ string thisElement = "";
+ if (String.IsNullOrEmpty(root.Name))
+ thisElement = "[Anonymous]";
+ else
+ thisElement = string.Format("[{0}]", root.Name);
+
+ thisElement += string.Format(" : {0}", root.GetType().Name);
+
+ results.Add(prefix + thisElement);
+ foreach (FrameworkElement directChild in root.GetVisualChildren())
+ {
+ directChild.GetChildTree(prefix + addPrefix, addPrefix, results);
+ }
+ }
+
+ ///
+ /// Provides a debug string that represents the visual child tree
+ ///
+ /// The root node
+ /// StringBuilder into which the text is appended
+ /// This method only works in DEBUG mode
+ [Conditional("DEBUG")]
+ public static void GetAncestorVisualTreeDebugText(this FrameworkElement node, StringBuilder result)
+ {
+ List tree = new List();
+ node.GetAncestorVisualTree(tree);
+ string prefix = "";
+ foreach (string s in tree)
+ {
+ result.AppendLine(prefix + s);
+ prefix = prefix + " ";
+ }
+ }
+
+ private static void GetAncestorVisualTree(this FrameworkElement node, List children)
+ {
+ string name = String.IsNullOrEmpty(node.Name) ? "[Anon]" : node.Name;
+ string thisNode = name + ": " + node.GetType().Name;
+
+ // Ensure list is in reverse order going up the tree
+ children.Insert(0, thisNode);
+ FrameworkElement parent = node.GetVisualParent();
+ if (parent != null)
+ GetAncestorVisualTree(parent, children);
+ }
+
+ ///
+ /// Returns a render transform of the specified type from the element, creating it if necessary
+ ///
+ /// The type of transform (Rotate, Translate, etc)
+ /// The element to check
+ /// The mode to use for creating transforms, if not found
+ /// The specified transform, or null if not found and not created
+ public static TRequestedTransform GetTransform(this UIElement element, TransformCreationMode mode) where TRequestedTransform : Transform, new()
+ {
+ Transform originalTransform = element.RenderTransform;
+ TRequestedTransform requestedTransform = null;
+ MatrixTransform matrixTransform = null;
+ TransformGroup transformGroup = null;
+
+ // Current transform is null -- create if necessary and return
+ if (originalTransform == null)
+ {
+ if ((mode & TransformCreationMode.Create) == TransformCreationMode.Create)
+ {
+ requestedTransform = new TRequestedTransform();
+ element.RenderTransform = requestedTransform;
+ return requestedTransform;
+ }
+
+ return null;
+ }
+
+ // Transform is exactly what we want -- return it
+ requestedTransform = originalTransform as TRequestedTransform;
+ if (requestedTransform != null)
+ return requestedTransform;
+
+
+ // The existing transform is matrix transform - overwrite if necessary and return
+ matrixTransform = originalTransform as MatrixTransform;
+ if (matrixTransform != null)
+ {
+ if (matrixTransform.Matrix.IsIdentity
+ && (mode & TransformCreationMode.Create) == TransformCreationMode.Create
+ && (mode & TransformCreationMode.IgnoreIdentityMatrix) == TransformCreationMode.IgnoreIdentityMatrix)
+ {
+ requestedTransform = new TRequestedTransform();
+ element.RenderTransform = requestedTransform;
+ return requestedTransform;
+ }
+
+ return null;
+ }
+
+ // Transform is actually a group -- check for the requested type
+ transformGroup = originalTransform as TransformGroup;
+ if (transformGroup != null)
+ {
+ foreach (Transform child in transformGroup.Children)
+ {
+ // Child is the right type -- return it
+ if (child is TRequestedTransform)
+ return child as TRequestedTransform;
+ }
+
+ // Right type was not found, but we are OK to add it
+ if ((mode & TransformCreationMode.AddToGroup) == TransformCreationMode.AddToGroup)
+ {
+ requestedTransform = new TRequestedTransform();
+ transformGroup.Children.Add(requestedTransform);
+ return requestedTransform;
+ }
+
+ return null;
+ }
+
+ // Current ransform is not a group and is not what we want;
+ // create a new group containing the existing transform and the new one
+ if ((mode & TransformCreationMode.CombineIntoGroup) == TransformCreationMode.CombineIntoGroup)
+ {
+ transformGroup = new TransformGroup();
+ transformGroup.Children.Add(originalTransform);
+ transformGroup.Children.Add(requestedTransform);
+ element.RenderTransform = transformGroup;
+ return requestedTransform;
+ }
+
+ Debug.Assert(false, "Shouldn't get here");
+ return null;
+ }
+
+ ///
+ /// Returns a string representation of a property path needed to update a Storyboard
+ ///
+ /// The element to get the path for
+ /// The property of the transform
+ /// The type of transform to look fo
+ /// A property path
+ public static string GetTransformPropertyPath(this FrameworkElement element, string subProperty) where TRequestedType : Transform
+ {
+ Transform t = element.RenderTransform;
+ if (t is TRequestedType)
+ return String.Format("(RenderTransform).({0}.{1})", typeof(TRequestedType).Name, subProperty);
+
+ else if (t is TransformGroup)
+ {
+ TransformGroup g = t as TransformGroup;
+ for (int i = 0; i < g.Children.Count; i++)
+ {
+ if (g.Children[i] is TRequestedType)
+ return String.Format("(RenderTransform).(TransformGroup.Children)[" + i + "].({0}.{1})",
+ typeof(TRequestedType).Name, subProperty);
+ }
+ }
+
+ return "";
+ }
+
+ ///
+ /// Returns a plane projection, creating it if necessary
+ ///
+ /// The element
+ /// Whether or not to create the projection if it doesn't already exist
+ /// The plane project, or null if not found / created
+ public static PlaneProjection GetPlaneProjection(this UIElement element, bool create)
+ {
+ Projection originalProjection = element.Projection;
+ PlaneProjection projection = null;
+
+ // Projection is already a plane projection; return it
+ if (originalProjection is PlaneProjection)
+ return originalProjection as PlaneProjection;
+
+ // Projection is null; create it if necessary
+ if (originalProjection == null)
+ {
+ if (create)
+ {
+ projection = new PlaneProjection();
+ element.Projection = projection;
+ }
+ }
+
+ // Note that if the project is a Matrix projection, it will not be
+ // changed and null will be returned.
+ return projection;
+ }
+ }
+
+ ///
+ /// Possible modes for creating a transform
+ ///
+ [Flags]
+ public enum TransformCreationMode
+ {
+ ///
+ /// Don't try and create a transform if it doesn't already exist
+ ///
+ None = 0,
+
+ ///
+ /// Create a transform if none exists
+ ///
+ Create = 1,
+
+ ///
+ /// Create and add to an existing group
+ ///
+ AddToGroup = 2,
+
+ ///
+ /// Create a group and combine with existing transform; may break existing animations
+ ///
+ CombineIntoGroup = 4,
+
+ ///
+ /// Treat identity matrix as if it wasn't there; may break existing animations
+ ///
+ IgnoreIdentityMatrix = 8,
+
+ ///
+ /// Create a new transform or add to group
+ ///
+ CreateOrAddAndIgnoreMatrix = Create | AddToGroup | IgnoreIdentityMatrix,
+
+ ///
+ /// Default behaviour, equivalent to CreateOrAddAndIgnoreMatrix
+ ///
+ Default = CreateOrAddAndIgnoreMatrix,
+ }
+}
\ No newline at end of file
diff --git a/ReadMe.md b/ReadMe.md
new file mode 100644
index 0000000..cff34e8
--- /dev/null
+++ b/ReadMe.md
@@ -0,0 +1,21 @@
+**Compass VO**
+
+Compass application for windows phone (8, 8.1) and windows mobile 10 that features an ***adjustable background map*** from a photo or a selected picture.
+The compass needle works properly only on devices that contains magnetometer sensor.
+
+**Quick start**
+
+For direct use on device just search 'Compass VO' in Microsoft Application Store and install the app.
+
+For further customization and extra features development, get the sources, enhance it, then please share with us.
+
+**Screenshots**
+
+
+
+
+
+
+
+
+
diff --git a/_media/capture_01.png b/_media/capture_01.png
new file mode 100644
index 0000000..a640419
Binary files /dev/null and b/_media/capture_01.png differ
diff --git a/_media/capture_02.png b/_media/capture_02.png
new file mode 100644
index 0000000..98e2feb
Binary files /dev/null and b/_media/capture_02.png differ
diff --git a/_media/capture_03.png b/_media/capture_03.png
new file mode 100644
index 0000000..04274ab
Binary files /dev/null and b/_media/capture_03.png differ
diff --git a/_media/capture_08.png b/_media/capture_08.png
new file mode 100644
index 0000000..7057d74
Binary files /dev/null and b/_media/capture_08.png differ
diff --git a/_media/capture_09.png b/_media/capture_09.png
new file mode 100644
index 0000000..8fddf90
Binary files /dev/null and b/_media/capture_09.png differ