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 + + + ° + + + + + + + + + + + N + + + + \ 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 + + + ° + + + + + + + + + + + N + + + + \ 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** + +![Capture 01](_media/capture_01.png) +![Capture 09](_media/capture_09.png) +![Capture 02](_media/capture_02.png) +![Capture 03](_media/capture_03.png) +![Capture 08](_media/capture_08.png) + + + 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