From 49a0a3eac94abe7f346c1681a7e89479c617b11f Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EC=95=88=EC=A3=BC=EC=9B=90/Common=20Platform=20Lab=28SR=29?= =?utf8?q?/Principal=20Engineer/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Thu, 26 Mar 2020 16:44:03 +0900 Subject: [PATCH] Add Under Armour sample app (#166) * Add Under Armour sample app * Change the file mode --- XSF.sln | 54 +++ .../MapMyRun/MapMyRun.Tests/DispatcherTest.cs | 177 +++++++++ .../MapMyRun/MapMyRun.Tests/LoadingTest.cs | 282 +++++++++++++++ .../MapMyRun/MapMyRun.Tests/MapMyRun.Tests.csproj | 18 + .../MapMyRun.Tests/PermissionManagerTest.cs | 37 ++ .../MapMyRun/MapMyRun.Tests/PhoneServiceTest.cs | 129 +++++++ .../MapMyRun/MapMyRun.Tests/SettingManagerTest.cs | 105 ++++++ .../MapMyRun/MapMyRun.Tests/WorkoutTest.cs | 294 +++++++++++++++ test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml | 46 +++ .../MapMyRun/MapMyRun.Tizen/App.xaml.cs | 56 +++ .../MapMyRun/MapMyRun.Tizen/MapMyRun.Tizen.csproj | 26 ++ .../MapMyRun/MapMyRun.Tizen/MapMyRun.cs | 23 ++ .../MapMyRun/MapMyRun.Tizen/Models/AppLauncher.cs | 25 ++ .../MapMyRun.Tizen/Models/MessagePortHandler.cs | 155 ++++++++ .../MapMyRun.Tizen/Models/TizenPlatformService.cs | 22 ++ .../MapMyRun.Tizen/ViewModels/BasePageModel.cs | 32 ++ .../MapMyRun.Tizen/ViewModels/LoadingPageModel.cs | 44 +++ .../MapMyRun.Tizen/ViewModels/MainPageModel.cs | 72 ++++ .../MapMyRun.Tizen/ViewModels/SettingsPageModel.cs | 69 ++++ .../ViewModels/WorkoutMain/AverageHrViewModel.cs | 72 ++++ .../ViewModels/WorkoutMain/CadenceViewModel.cs | 70 ++++ .../ViewModels/WorkoutMain/CaloriesViewModel.cs | 70 ++++ .../ViewModels/WorkoutMain/CurrentHrViewModel.cs | 72 ++++ .../ViewModels/WorkoutMain/CurrentPaceViewModel.cs | 70 ++++ .../ViewModels/WorkoutMain/DistanceViewModel.cs | 70 ++++ .../ViewModels/WorkoutMain/DurationViewModel.cs | 98 +++++ .../WorkoutMain/IWorkoutItemViewModel.cs | 20 + .../ViewModels/WorkoutMain/PeakHrViewModel.cs | 72 ++++ .../ViewModels/WorkoutMain/PeakPaceViewModel.cs | 70 ++++ .../ViewModels/WorkoutMain/WorkoutMainPageModel.cs | 97 +++++ .../ViewModels/WorkoutMain/ZoneHrViewModel.cs | 70 ++++ .../ViewModels/WorkoutSetupPageModel.cs | 72 ++++ .../MapMyRun/MapMyRun.Tizen/res/BPMIcon.png | Bin 0 -> 682 bytes .../MapMyRun/MapMyRun.Tizen/res/GPSIcon.png | Bin 0 -> 988 bytes .../MapMyRun/MapMyRun.Tizen/res/UAIcon.png | Bin 0 -> 3552 bytes .../MapMyRun.Tizen/res/backgroundBottomLight.png | Bin 0 -> 24335 bytes .../MapMyRun.Tizen/res/backgroundWholeLight.png | Bin 0 -> 5819 bytes .../MapMyRun.Tizen/res/finish-btn-pressed.png | Bin 0 -> 3879 bytes .../MapMyRun/MapMyRun.Tizen/res/finish-btn.png | Bin 0 -> 3879 bytes .../MapMyRun/MapMyRun.Tizen/res/hero-bg.png | Bin 0 -> 49210 bytes .../MapMyRun.Tizen/res/icon-gps--connected.png | Bin 0 -> 817 bytes .../MapMyRun.Tizen/res/icon-gps--connecting.png | Bin 0 -> 534 bytes .../MapMyRun.Tizen/res/icon-gps--error.png | Bin 0 -> 865 bytes .../MapMyRun.Tizen/res/icon-gps--not-connected.png | Bin 0 -> 825 bytes .../MapMyRun.Tizen/res/icon-shoes--connected.png | Bin 0 -> 1131 bytes .../MapMyRun.Tizen/res/icon-shoes--connecting.png | Bin 0 -> 1089 bytes .../MapMyRun.Tizen/res/icon-shoes--error.png | Bin 0 -> 1400 bytes .../res/icon-shoes--not-connected.png | Bin 0 -> 1141 bytes .../MapMyRun.Tizen/res/localization/en.json | 401 +++++++++++++++++++++ .../MapMyRun/MapMyRun.Tizen/res/logo--tray.png | Bin 0 -> 2112 bytes .../MapMyRun.Tizen/res/select-btn-empty.png | Bin 0 -> 3162 bytes .../MapMyRun/MapMyRun.Tizen/res/splash.circle.png | Bin 0 -> 116083 bytes .../MapMyRun.Tizen/res/start-btn-empty.png | Bin 0 -> 3879 bytes .../MapMyRun.Tizen/shared/res/MapMyRun.png | Bin 0 -> 116083 bytes .../MapMyRun/MapMyRun.Tizen/tizen-manifest.xml | 18 + .../views/CircleProgressBarSurfaceItemExtension.cs | 41 +++ .../MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml | 29 ++ .../MapMyRun.Tizen/views/LoadingPage.xaml.cs | 49 +++ .../MapMyRun/MapMyRun.Tizen/views/MainPage.xaml | 68 ++++ .../MapMyRun/MapMyRun.Tizen/views/MainPage.xaml.cs | 168 +++++++++ .../MapMyRun.Tizen/views/SelectWorkoutPage.xaml | 33 ++ .../MapMyRun.Tizen/views/SelectWorkoutPage.xaml.cs | 216 +++++++++++ .../MapMyRun.Tizen/views/SetDurationPage.xaml | 62 ++++ .../MapMyRun.Tizen/views/SetDurationPage.xaml.cs | 111 ++++++ .../MapMyRun.Tizen/views/SettingsPage.xaml | 55 +++ .../MapMyRun.Tizen/views/SettingsPage.xaml.cs | 96 +++++ .../MapMyRun.Tizen/views/Templates/SettingCell.cs | 61 ++++ .../views/Templates/SettingCellTemplateSelector.cs | 19 + .../MapMyRun.Tizen/views/WorkoutHistoryPage.xaml | 41 +++ .../views/WorkoutHistoryPage.xaml.cs | 51 +++ .../MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml | 7 + .../views/WorkoutMainIndexPage.xaml.cs | 126 +++++++ .../views/WorkoutMainLoadingPage.xaml | 22 ++ .../views/WorkoutMainLoadingPage.xaml.cs | 95 +++++ .../MapMyRun.Tizen/views/WorkoutMainPage.xaml | 14 + .../MapMyRun.Tizen/views/WorkoutMainPage.xaml.cs | 297 +++++++++++++++ .../MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml | 14 + .../views/WorkoutMainPageGPS.xaml.cs | 165 +++++++++ .../MapMyRun.Tizen/views/WorkoutSetupPage.xaml | 39 ++ .../MapMyRun.Tizen/views/WorkoutSetupPage.xaml.cs | 64 ++++ test/UnderArmour/MapMyRun/MapMyRun.sln | 36 ++ .../MapMyRun/MapMyRun/MapMyRun.projitems | 51 +++ test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.shproj | 13 + .../MapMyRun/MapMyRun/Models/Activity.cs | 30 ++ .../MapMyRun/MapMyRun/Models/ActivityRepository.cs | 267 ++++++++++++++ .../MapMyRun/MapMyRun/Models/Dispatcher.cs | 125 +++++++ .../MapMyRun/MapMyRun/Models/IAppLauncher.cs | 11 + .../MapMyRun/MapMyRun/Models/IDispatcher.cs | 18 + .../MapMyRun/Models/IMessagePortHandler.cs | 18 + .../MapMyRun/MapMyRun/Models/IPermissionManager.cs | 21 ++ .../MapMyRun/MapMyRun/Models/IPhoneService.cs | 23 ++ .../MapMyRun/MapMyRun/Models/IPlatformService.cs | 13 + .../MapMyRun/MapMyRun/Models/Loading.cs | 316 ++++++++++++++++ .../MapMyRun/Models/NotificationObserver.cs | 44 +++ .../MapMyRun/Models/NotificationProvider.cs | 50 +++ .../MapMyRun/MapMyRun/Models/PermissionManager.cs | 81 +++++ .../MapMyRun/MapMyRun/Models/PhoneService.cs | 111 ++++++ .../MapMyRun/MapMyRun/Models/Request.cs | 129 +++++++ .../MapMyRun/MapMyRun/Models/Response.cs | 28 ++ .../MapMyRun/Models/Settings/ISettingManager.cs | 13 + .../MapMyRun/Models/Settings/SettingManager.cs | 168 +++++++++ .../MapMyRun/Models/Settings/SettingsData.cs | 40 ++ .../Models/Workout/Items/AverageCadence.cs | 87 +++++ .../MapMyRun/Models/Workout/Items/AverageHr.cs | 76 ++++ .../MapMyRun/Models/Workout/Items/AveragePace.cs | 88 +++++ .../MapMyRun/Models/Workout/Items/AverageSpeed.cs | 87 +++++ .../MapMyRun/Models/Workout/Items/Calories.cs | 73 ++++ .../Models/Workout/Items/CurrentCadence.cs | 86 +++++ .../MapMyRun/Models/Workout/Items/CurrentHr.cs | 77 ++++ .../MapMyRun/Models/Workout/Items/CurrentPace.cs | 87 +++++ .../MapMyRun/Models/Workout/Items/CurrentSpeed.cs | 86 +++++ .../MapMyRun/Models/Workout/Items/Distance.cs | 80 ++++ .../MapMyRun/Models/Workout/Items/Duration.cs | 80 ++++ .../MapMyRun/Models/Workout/Items/IWorkoutItem.cs | 66 ++++ .../MapMyRun/Models/Workout/Items/MaxPace.cs | 87 +++++ .../MapMyRun/Models/Workout/Items/MaxSpeed.cs | 86 +++++ .../MapMyRun/Models/Workout/Items/PeakHr.cs | 76 ++++ .../Models/Workout/Items/WorkoutItemFactory.cs | 49 +++ .../MapMyRun/Models/Workout/Items/ZoneHr.cs | 75 ++++ .../MapMyRun/MapMyRun/Models/Workout/Workout.cs | 329 +++++++++++++++++ .../MapMyRun/Models/Workout/WorkoutData.cs | 66 ++++ .../MapMyRunService.Test/AccountManagerTest.cs | 82 +++++ .../MapMyRunService/MapMyRunService.Test/DB.cs | 32 ++ .../MapMyRunService.Test/DispatcherTest.cs | 177 +++++++++ .../MapMyRunService.Test/KeepAliveTest.cs | 50 +++ .../MapMyRunService.Tests.csproj | 21 ++ .../MapMyRunService.Tizen.csproj | 24 ++ .../MapMyRunService.Tizen/MapMyRunService_App.cs | 85 +++++ .../MapMyRunService.Tizen/Models/AuthCache.cs | 77 ++++ .../MapMyRunService.Tizen/Models/Database/DB.cs | 35 ++ .../Models/MessagePortHandler.cs | 171 +++++++++ .../Models/Settings/GPSManager.cs | 38 ++ .../Models/TizenServiceApplication.cs | 24 ++ .../Services/SensorService.cs | 280 ++++++++++++++ .../Services/WorkoutService.cs | 104 ++++++ .../shared/res/MapMyRunService.png | Bin 0 -> 10097 bytes .../MapMyRunService.Tizen/tizen-manifest.xml | 19 + .../MapMyRunService/MapMyRunService.sln | 33 ++ .../MapMyRunService/MapMyRunService.projitems | 42 +++ .../MapMyRunService/MapMyRunService.shproj | 13 + .../Models/Account/AccountManager.cs | 92 +++++ .../MapMyRunService/Models/Account/User.cs | 31 ++ .../MapMyRunService/Models/Account/UserDao.cs | 120 ++++++ .../MapMyRunService/Models/Auth/Authentication.cs | 66 ++++ .../Models/Auth/AuthenticationToken.cs | 11 + .../Models/Auth/ExampleAuthProvider.cs | 72 ++++ .../MapMyRunService/Models/Auth/IAuthCache.cs | 15 + .../Models/Auth/IAuthenticationProvider.cs | 9 + .../MapMyRunService/Models/Dispatcher.cs | 147 ++++++++ .../MapMyRunService/Models/HttpService.cs | 31 ++ .../MapMyRunService/MapMyRunService/Models/IDB.cs | 12 + .../MapMyRunService/Models/IDispatcher.cs | 22 ++ .../MapMyRunService/Models/IMessagePortHandler.cs | 20 + .../Models/ITizenServiceApplication.cs | 11 + .../MapMyRunService/Models/KeepAlive/KeepAlive.cs | 51 +++ .../MapMyRunService/Models/Loading.cs | 102 ++++++ .../MapMyRunService/Models/NotificationObserver.cs | 44 +++ .../MapMyRunService/Models/NotificationProvider.cs | 50 +++ .../MapMyRunService/Models/Request.cs | 129 +++++++ .../MapMyRunService/Models/Response.cs | 28 ++ .../MapMyRunService/Models/Settings/IGPSManager.cs | 22 ++ .../Models/Settings/SettingsData.cs | 40 ++ .../Models/Settings/SettingsManager.cs | 133 +++++++ .../Models/Workout/HeartRateData.cs | 49 +++ .../MapMyRunService/Models/Workout/LocationData.cs | 13 + .../Models/Workout/PedometerData.cs | 17 + .../MapMyRunService/Models/Workout/WorkoutData.cs | 65 ++++ .../Models/Workout/WorkoutManager.cs | 209 +++++++++++ .../Models/Workout/WorkoutReport.cs | 25 ++ 169 files changed, 11241 insertions(+) create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tests/DispatcherTest.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tests/LoadingTest.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tests/MapMyRun.Tests.csproj create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tests/PermissionManagerTest.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tests/PhoneServiceTest.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tests/SettingManagerTest.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tests/WorkoutTest.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.Tizen.csproj create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/AppLauncher.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/MessagePortHandler.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/TizenPlatformService.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/BasePageModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/LoadingPageModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/MainPageModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/SettingsPageModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/AverageHrViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CadenceViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CaloriesViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentHrViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentPaceViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DistanceViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DurationViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/IWorkoutItemViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakHrViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakPaceViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/WorkoutMainPageModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/ZoneHrViewModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutSetupPageModel.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/BPMIcon.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/GPSIcon.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/UAIcon.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/backgroundBottomLight.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/backgroundWholeLight.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/finish-btn-pressed.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/finish-btn.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/hero-bg.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-gps--connected.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-gps--connecting.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-gps--error.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-gps--not-connected.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--connected.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--connecting.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--error.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--not-connected.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/localization/en.json create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/logo--tray.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/select-btn-empty.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/splash.circle.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/start-btn-empty.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/shared/res/MapMyRun.png create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/tizen-manifest.xml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/CircleProgressBarSurfaceItemExtension.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCell.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCellTemplateSelector.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun.sln create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.projitems create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.shproj create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Activity.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/ActivityRepository.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Dispatcher.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/IAppLauncher.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/IDispatcher.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/IMessagePortHandler.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/IPermissionManager.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/IPhoneService.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/IPlatformService.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Loading.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationObserver.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationProvider.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/PermissionManager.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/PhoneService.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Request.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Response.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/ISettingManager.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingManager.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingsData.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageCadence.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageHr.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AveragePace.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageSpeed.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Calories.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentCadence.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentHr.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentPace.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentSpeed.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Distance.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Duration.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/IWorkoutItem.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxPace.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxSpeed.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/PeakHr.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/WorkoutItemFactory.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/ZoneHr.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Workout.cs create mode 100755 test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/WorkoutData.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Test/AccountManagerTest.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Test/DB.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Test/DispatcherTest.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Test/KeepAliveTest.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Test/MapMyRunService.Tests.csproj create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService.Tizen.csproj create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService_App.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/AuthCache.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Database/DB.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/MessagePortHandler.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Settings/GPSManager.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/TizenServiceApplication.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/SensorService.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/WorkoutService.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/shared/res/MapMyRunService.png create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/tizen-manifest.xml create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService.sln create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.projitems create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.shproj create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/AccountManager.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/User.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/UserDao.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/Authentication.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/AuthenticationToken.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/ExampleAuthProvider.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthCache.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthenticationProvider.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Dispatcher.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/HttpService.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDB.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDispatcher.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/IMessagePortHandler.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/ITizenServiceApplication.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/KeepAlive/KeepAlive.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Loading.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationObserver.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationProvider.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Request.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Response.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/IGPSManager.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsData.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsManager.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/HeartRateData.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/LocationData.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/PedometerData.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutData.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutManager.cs create mode 100755 test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutReport.cs diff --git a/XSF.sln b/XSF.sln index e28e0c9..69f730d 100644 --- a/XSF.sln +++ b/XSF.sln @@ -38,7 +38,30 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Weather", "test\Weather\Wea EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XStopWatch", "test\XStopWatch\XStopWatch.csproj", "{A689445F-79CA-4843-BB47-B39860C863CD}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnderArmour", "UnderArmour", "{50836253-D1BC-4BCC-B968-52974065F166}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MapMyRun", "MapMyRun", "{7295CB65-4F82-425B-A5CC-5DA241D5F6F1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MapMyRunService", "MapMyRunService", "{DCF6FC4F-A868-42A2-AEDA-DFD737B670B4}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "MapMyRun", "test\UnderArmour\MapMyRun\MapMyRun\MapMyRun.shproj", "{3C82C042-F1F4-42E3-B82D-DFED5E06EE19}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapMyRun.Tizen", "test\UnderArmour\MapMyRun\MapMyRun.Tizen\MapMyRun.Tizen.csproj", "{017E4C5E-4CF8-4A84-898A-5045BF22646C}" + ProjectSection(ProjectDependencies) = postProject + {4D093123-296A-4471-A97F-58E5F8F2948D} = {4D093123-296A-4471-A97F-58E5F8F2948D} + EndProjectSection +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "MapMyRunService", "test\UnderArmour\MapMyRunService\MapMyRunService\MapMyRunService.shproj", "{7B89FD19-6DCD-4721-9000-FA612161C492}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapMyRunService.Tizen", "test\UnderArmour\MapMyRunService\MapMyRunService.Tizen\MapMyRunService.Tizen.csproj", "{09C952B3-BE3F-447E-838F-CD9D14A76D09}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + test\UnderArmour\MapMyRun\MapMyRun\MapMyRun.projitems*{017e4c5e-4cf8-4a84-898a-5045bf22646c}*SharedItemsImports = 5 + test\UnderArmour\MapMyRunService\MapMyRunService\MapMyRunService.projitems*{09c952b3-be3f-447e-838f-cd9d14a76d09}*SharedItemsImports = 5 + test\UnderArmour\MapMyRun\MapMyRun\MapMyRun.projitems*{3c82c042-f1f4-42e3-b82d-dfed5e06ee19}*SharedItemsImports = 13 + test\UnderArmour\MapMyRunService\MapMyRunService\MapMyRunService.projitems*{7b89fd19-6dcd-4721-9000-fa612161c492}*SharedItemsImports = 13 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 @@ -156,6 +179,30 @@ Global {A689445F-79CA-4843-BB47-B39860C863CD}.Release|x64.Build.0 = Release|Any CPU {A689445F-79CA-4843-BB47-B39860C863CD}.Release|x86.ActiveCfg = Release|Any CPU {A689445F-79CA-4843-BB47-B39860C863CD}.Release|x86.Build.0 = Release|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Debug|x64.ActiveCfg = Debug|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Debug|x64.Build.0 = Debug|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Debug|x86.ActiveCfg = Debug|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Debug|x86.Build.0 = Debug|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Release|Any CPU.Build.0 = Release|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Release|x64.ActiveCfg = Release|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Release|x64.Build.0 = Release|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Release|x86.ActiveCfg = Release|Any CPU + {017E4C5E-4CF8-4A84-898A-5045BF22646C}.Release|x86.Build.0 = Release|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Debug|x64.ActiveCfg = Debug|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Debug|x64.Build.0 = Debug|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Debug|x86.ActiveCfg = Debug|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Debug|x86.Build.0 = Debug|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Release|Any CPU.Build.0 = Release|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Release|x64.ActiveCfg = Release|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Release|x64.Build.0 = Release|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Release|x86.ActiveCfg = Release|Any CPU + {09C952B3-BE3F-447E-838F-CD9D14A76D09}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -167,6 +214,13 @@ Global {7A4D58D3-EF64-46FC-B293-75F71D9938DF} = {BCEBC994-EAB5-4142-B60C-58FED3DFC835} {198A2943-3091-46FB-B967-B85D26496025} = {BCEBC994-EAB5-4142-B60C-58FED3DFC835} {A689445F-79CA-4843-BB47-B39860C863CD} = {BCEBC994-EAB5-4142-B60C-58FED3DFC835} + {50836253-D1BC-4BCC-B968-52974065F166} = {BCEBC994-EAB5-4142-B60C-58FED3DFC835} + {7295CB65-4F82-425B-A5CC-5DA241D5F6F1} = {50836253-D1BC-4BCC-B968-52974065F166} + {DCF6FC4F-A868-42A2-AEDA-DFD737B670B4} = {50836253-D1BC-4BCC-B968-52974065F166} + {3C82C042-F1F4-42E3-B82D-DFED5E06EE19} = {7295CB65-4F82-425B-A5CC-5DA241D5F6F1} + {017E4C5E-4CF8-4A84-898A-5045BF22646C} = {7295CB65-4F82-425B-A5CC-5DA241D5F6F1} + {7B89FD19-6DCD-4721-9000-FA612161C492} = {DCF6FC4F-A868-42A2-AEDA-DFD737B670B4} + {09C952B3-BE3F-447E-838F-CD9D14A76D09} = {DCF6FC4F-A868-42A2-AEDA-DFD737B670B4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4312D28C-6473-4391-B1AF-AAAFAF4FA0C1} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tests/DispatcherTest.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tests/DispatcherTest.cs new file mode 100755 index 0000000..9887739 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tests/DispatcherTest.cs @@ -0,0 +1,177 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRun.Models; +using System.Collections.Generic; + +namespace MapMyRun.Tests +{ + [TestFixture] + class DispatcherTest + { + Mock mockMessagePortHandler = null; + + [SetUp] + public void Setup() + { + mockMessagePortHandler = new Mock(); + } + + [Test] + public void Initialize_Called_CallMessagePortHandlerConnect() + { + mockMessagePortHandler.Setup(x => x.Connect(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + + var dispatcher = Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + + mockMessagePortHandler.Verify(x => x.Connect(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + } + + [Test] + public void Initialize_Called_AddOnReceiveMessage() + { + var dispatcher = Dispatcher.Instance; + + mockMessagePortHandler.SetupAdd(m => m.MessageReceived += dispatcher.OnReceiveMessage); + + dispatcher.Initialize(mockMessagePortHandler.Object); + mockMessagePortHandler.VerifyAdd(m => m.MessageReceived += dispatcher.OnReceiveMessage, Times.Once()); + } + + [Test] + public void SendRequest_FirstCalled_CallMessagePortHandlerSend() + { + var requestData = "banana"; + var request = new Mock(OperationType.PrepareWKO, requestData); + Dictionary requestDataSet = null; + + mockMessagePortHandler.Setup(x => x.Send(It.IsAny>())) + .Callback>(arg => { + requestDataSet = arg; + } + ); + + var dispatcher = Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + + dispatcher.SendRequest(request.Object); + requestDataSet.TryGetValue(Dispatcher.OperationKey, out string operation); + requestDataSet.TryGetValue(Dispatcher.DataKey, out string data); + + mockMessagePortHandler.Verify(x => x.Send(It.IsAny>()), Times.Once()); + Assert.AreEqual(((int)OperationType.PrepareWKO).ToString(), operation); + Assert.AreEqual(requestData, data); + } + + [Test] + public void OnReceiveMessage_Called_CallCorrectRequestOnReceiveResponse() + { + string requestData = "orange"; + var request = new Request(OperationType.PrepareWKO, requestData); + bool isCalled = false; + request.ResponseReceived += (Response data) => + { + isCalled = true; + }; + + var dispatcher = Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var tid = dispatcher.SendRequest(request); + + var responseData = new Dictionary + { + { Dispatcher.TransIdKey, tid} + }; + + dispatcher.OnReceiveMessage(responseData); + Assert.IsTrue(isCalled); + } + + [Test] + public void OnReceiveMessage_Called_DontCallIncorrectRequestOnReceiveResponse() + { + string requestData = "orange"; + var request = new Request(OperationType.PrepareWKO, requestData); + var isCalled = false; + request.ResponseReceived += (Response data) => + { + isCalled = true; + }; + + var dispatcher = Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var tid = dispatcher.SendRequest(request); + + var responseData = new Dictionary + { + { Dispatcher.TransIdKey, tid + "Never"} + }; + + dispatcher.OnReceiveMessage(responseData); + Assert.IsFalse(isCalled); + } + + [Test] + public void OnReceiveMessage_WithOperation_DontCallRequestOnReceiveResponse() + { + string requestData = "orange"; + var request = new Request(OperationType.PrepareWKO, requestData); + var isCalled = false; + request.ResponseReceived += (Response data) => + { + isCalled = true; + }; + + var dispatcher = Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var tid = dispatcher.SendRequest(request); + + var responseData = new Dictionary + { + { Dispatcher.OperationKey, "kiwi" }, + { Dispatcher.TransIdKey, tid} + }; + + dispatcher.OnReceiveMessage(responseData); + Assert.IsFalse(isCalled); + } + + [Test] + public void OnReceiveMessage_WithoutOperation_ProvideCorrectNotification() + { + var isCalled = false; + var mockObserver = new NotificationObserver(OperationType.ApplicationInitialized); + mockObserver.NotificationReceived += (string data) => + { + isCalled = true; + }; + + var dispatcher = Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + + var monitor = dispatcher.Listen(OperationType.ApplicationInitialized); + mockObserver.Subscribe(monitor); + + Dictionary responseData = new Dictionary + { + { Dispatcher.TransIdKey, "orange"}, + { Dispatcher.DataKey, "apple"}, + { Dispatcher.OperationKey, "46"} + }; + dispatcher.OnReceiveMessage(responseData); + + Assert.IsTrue(isCalled); + } + + [Test] + public void Listen_Called_ReturnCorrectNotificationProvider() + { + var dispatcher = Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var notificationMonitor = dispatcher.Listen(OperationType.ApplicationInitialized); + Assert.AreEqual(notificationMonitor.Type, OperationType.ApplicationInitialized); + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tests/LoadingTest.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tests/LoadingTest.cs new file mode 100755 index 0000000..7a121ad --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tests/LoadingTest.cs @@ -0,0 +1,282 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRun.Models; +using MapMyRun.Models.Settings; + +namespace MapMyRun.Tests +{ + [TestFixture] + public class LoadingTests + { + Mock mockAppLauncher = null; + Mock mockDispatcher = null; + Mock mockPhoneService = null; + Mock mockMessagePortHandler = null; + Mock mockSettingManager = null; + + [SetUp] + public void Setup() + { + mockAppLauncher = new Mock(); + mockDispatcher = new Mock(); + mockPhoneService = new Mock(); + mockMessagePortHandler = new Mock(); + mockSettingManager = new Mock(); + } + + [TearDown] + public void Cleanup() + { + } + + [Test] + public void StartLoading_Called_CallIsPlatformSwVersionSupported() + { + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "orange"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + mockPhoneService.Verify(x => x.IsPlatformSwVersionSupported(), Times.Once()); + } + + [Test] + public void StartLoading_Called_CallKeepScreenOn() + { + mockPhoneService.Setup(x => x.KeepScreenOn()); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "orange"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + mockPhoneService.Verify(x => x.KeepScreenOn(), Times.Once()); + } + + [Test] + public void StartLoading_Called_IsAuthenticated() + { + mockPhoneService.Setup(x => x.IsAuthenticated(It.IsAny>())); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "orange"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + mockPhoneService.Verify(x => x.IsAuthenticated(It.IsAny>()), Times.Once()); + } + + [Test] + public void StartLoading_Called_CallLaunchServiceApp() + { + mockAppLauncher.Setup(x => x.Launch("banana")); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + mockAppLauncher.Verify(x => x.Launch("banana"), Times.Once()); + } + + [Test] + public void StartLoading_NotSupportedPlatform_NotStartedState() + { + mockAppLauncher.Setup(x => x.Launch("banana")).Throws(new Exception()); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(false); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + Assert.AreEqual(LoadingState.Not_Started, loading.State); + } + + [Test] + public void StartLoading_LaunchThrowException_ConnectState() + { + mockAppLauncher.Setup(x => x.Launch("banana")).Throws(new Exception()); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + Assert.AreEqual(LoadingState.Connect, loading.State); + } + + [Test] + public void StartLoading_Called_CallDispatcherInitialize() + { + mockAppLauncher.Setup(x => x.Launch("banana")); + mockDispatcher.Setup(x => x.Initialize(It.IsAny())); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + mockDispatcher.Verify(x => x.Initialize(It.IsAny()), Times.Once()); + } + + [Test] + public void StartLoading_AuthFail_AuthState() + { + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + mockPhoneService.Setup(x => x.IsAuthenticated(It.IsAny>())) + .Callback>(callback => callback(PhoneApplicationState.NotLoggedIn)); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + Assert.AreEqual(LoadingState.Auth, loading.State); + } + + [Test] + public void StartLoading_AuthSuccess_FinishedState() + { + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + mockPhoneService.Setup(x => x.IsAuthenticated(It.IsAny>())) + .Callback>(callback => callback(PhoneApplicationState.LoggedIn)); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(LoadingState.Not_Started, loading.State); + loading.StartLoading(); + Assert.AreEqual(LoadingState.Finished, loading.State); + } + + [Test] + public void StartLoading_AuthSuccess_PhoneStateLoggedIn() + { + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + mockPhoneService.Setup(x => x.IsAuthenticated(It.IsAny>())) + .Callback>(callback => callback(PhoneApplicationState.LoggedIn)); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(PhoneApplicationState.NotSupportedTizenVersion, loading.phoneState); + loading.StartLoading(); + Assert.AreEqual(PhoneApplicationState.LoggedIn, loading.phoneState); + } + + [Test] + public void StartLoading_AuthSuccess_CallInitializeApp() + { + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + mockPhoneService.Setup(x => x.IsPlatformSwVersionSupported()).Returns(true); + mockPhoneService.Setup(x => x.InitializeApp()); + mockPhoneService.Setup(x => x.IsAuthenticated(It.IsAny>())) + .Callback>(callback => callback(PhoneApplicationState.LoggedIn)); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + + Assert.AreEqual(PhoneApplicationState.NotSupportedTizenVersion, loading.phoneState); + loading.StartLoading(); + mockPhoneService.Verify(x => x.InitializeApp(), Times.Once()); + } + + [Test] + public void InitializeLogoutEventHandler_Called_CallDispatcherListenWithLogoutType() + { + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + loading.InitializeLogoutEventHandler(); + + mockDispatcher.Verify(x => x.Listen(OperationType.Logout), Times.Once()); + } + + [Test] + public void InitializeAfterLoading_Called_CallPhoneServiceOnAppStart() + { + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + mockPhoneService.Setup(x => x.OnAppStart()); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + loading.InitializeAfterLoading(); + + mockPhoneService.Verify(x => x.OnAppStart(), Times.Once()); + } + + [Test] + public void InitializeAfterLoading_Called_CallSendRequestWithScontext() + { + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => { + if (arg.Operation == OperationType.CheckSContext) + request = arg; + } + ); + mockPhoneService.Setup(x => x.OnAppStart()); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + loading.InitializeAfterLoading(); + + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.AtLeastOnce()); + Assert.AreEqual(request.Operation, OperationType.CheckSContext); + Assert.IsTrue(request.Data.Equals("")); + } + + [Test] + public void InitializeAfterLoading_Called_CallSendRequestWithGpsAcquire() + { + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => { + if (arg.Operation == OperationType.StartGpsAcquire) + request = arg; + } + ); + mockPhoneService.Setup(x => x.OnAppStart()); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + loading.InitializeAfterLoading(); + + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.AtLeastOnce()); + Assert.AreEqual(request.Operation, OperationType.StartGpsAcquire); + Assert.IsTrue(request.Data.Equals("")); + } + + [Test] + public void InitializeAfterLoading_Called_CallSendRequestWithTrainingPlan() + { + mockDispatcher.Setup(x => x.Listen(OperationType.Logout)).Returns(new NotificationProvider(OperationType.Logout)); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => { + if (arg.Operation == OperationType.GetTodayTrainingPlans) + request = arg; + } + ); + mockPhoneService.Setup(x => x.OnAppStart()); + + var loading = new Loading(mockAppLauncher.Object, mockMessagePortHandler.Object, + mockDispatcher.Object, mockPhoneService.Object, mockSettingManager.Object, "banana"); + loading.InitializeAfterLoading(); + + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.AtLeastOnce()); + Assert.AreEqual(request.Operation, OperationType.GetTodayTrainingPlans); + Assert.IsTrue(request.Data.Equals("")); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tests/MapMyRun.Tests.csproj b/test/UnderArmour/MapMyRun/MapMyRun.Tests/MapMyRun.Tests.csproj new file mode 100755 index 0000000..14d6ec3 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tests/MapMyRun.Tests.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tests/PermissionManagerTest.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tests/PermissionManagerTest.cs new file mode 100755 index 0000000..e777abf --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tests/PermissionManagerTest.cs @@ -0,0 +1,37 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRun.Models; + +namespace MapMyRun.Tests +{ + [TestFixture] + class PermissionManagerTest + { + Mock mockDispatcher; + + [SetUp] + public void Setup() + { + mockDispatcher = new Mock(); + } + + [Test] + public void RequestPermission_Called_CallSendRequest() + { + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())) + .Callback(arg => + { + request = arg; + } + ); + + var permissionManager = new PermissionManager(mockDispatcher.Object); + permissionManager.RequestPermission(PermissionType.Location, null); + + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.AtLeastOnce()); + Assert.AreEqual(request.Operation, OperationType.RequestPermission); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tests/PhoneServiceTest.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tests/PhoneServiceTest.cs new file mode 100755 index 0000000..3af6c97 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tests/PhoneServiceTest.cs @@ -0,0 +1,129 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRun.Models; + +namespace MapMyRun.Tests +{ + [TestFixture] + class PhoneServiceTest + { + [SetUp] + public void Setup() + { + } + + [Test] + public void IsPlatformSwVersionSupported_SupportVersion_ReturnTrue() + { + var mockPlatformService = new Mock(); + var mockDispatcher = new Mock(); + var supportVersionA = "R360BPK5"; + var supportVersionB = "R360BPK6"; + var supportVersionC = "R360CPK5"; + var supportVersionD = "R380BPK3"; + + var phoneService = new PhoneService(mockDispatcher.Object, mockPlatformService.Object); + + mockPlatformService.Setup(x => x.GetSwVersion(out supportVersionA)); + Assert.AreEqual(true, phoneService.IsPlatformSwVersionSupported()); + + mockPlatformService.Setup(x => x.GetSwVersion(out supportVersionB)); + Assert.AreEqual(true, phoneService.IsPlatformSwVersionSupported()); + + mockPlatformService.Setup(x => x.GetSwVersion(out supportVersionC)); + Assert.AreEqual(true, phoneService.IsPlatformSwVersionSupported()); + + mockPlatformService.Setup(x => x.GetSwVersion(out supportVersionD)); + Assert.AreEqual(true, phoneService.IsPlatformSwVersionSupported()); + } + + [Test] + public void IsPlatformSwVersionSupported_NotSupportVersion_ReturnFalse() + { + var mockPlatformService = new Mock(); + var mockDispatcher = new Mock(); + var notSupportVersionA = "R360BPK4"; + var notSupportVersionB = "R360APK5"; + var notSupportVersionC = "R3601PK5"; + var notSupportVersionD = "R360BOK5"; + + var phoneService = new PhoneService(mockDispatcher.Object, mockPlatformService.Object); + + mockPlatformService.Setup(x => x.GetSwVersion(out notSupportVersionA)); + Assert.AreEqual(false, phoneService.IsPlatformSwVersionSupported()); + + mockPlatformService.Setup(x => x.GetSwVersion(out notSupportVersionB)); + Assert.AreEqual(false, phoneService.IsPlatformSwVersionSupported()); + + mockPlatformService.Setup(x => x.GetSwVersion(out notSupportVersionC)); + Assert.AreEqual(false, phoneService.IsPlatformSwVersionSupported()); + + mockPlatformService.Setup(x => x.GetSwVersion(out notSupportVersionD)); + Assert.AreEqual(false, phoneService.IsPlatformSwVersionSupported()); + } + + [Test] + public void KeepScreenOn_Called_CallKeepScreenOn() + { + var mockPlatformService = new Mock(); + var mockDispatcher = new Mock(); + mockPlatformService.Setup(x => x.KeepScreenOn()); + + var phoneService = new PhoneService(mockDispatcher.Object, mockPlatformService.Object); + + phoneService.KeepScreenOn(); + mockPlatformService.Verify(x => x.KeepScreenOn(), Times.Once()); + } + + [Test] + public void IsAuthenticated_Called_CallSendRequest() + { + var mockPlatformService = new Mock(); + var mockDispatcher = new Mock(); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => request = arg); + + var phoneService = new PhoneService(mockDispatcher.Object, mockPlatformService.Object); + + phoneService.IsAuthenticated(It.IsAny>()); + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.Once()); + Assert.AreEqual(request.Operation, OperationType.CheckIsAuthenticated); + Assert.IsTrue(request.Data.Equals("{ \"authenticate\": false }")); + } + + [Test] + public void InitializeApp_Called_CallSendRequest() + { + var mockPlatformService = new Mock(); + var mockDispatcher = new Mock(); + mockPlatformService.Setup(x => x.KeepScreenOn()); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => request = arg); + + var phoneService = new PhoneService(mockDispatcher.Object, mockPlatformService.Object); + + phoneService.InitializeApp(); + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.Once()); + Assert.AreEqual(request.Operation, OperationType.ApplicationInitialized); + Assert.IsTrue(request.Data.Equals("")); + } + + [Test] + public void OnAppStart_Called_CallSendRequest() + { + var mockPlatformService = new Mock(); + var mockDispatcher = new Mock(); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => request = arg); + + var phoneService = new PhoneService(mockDispatcher.Object, mockPlatformService.Object); + + phoneService.OnAppStart(); + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.Once()); + Assert.AreEqual(request.Operation, OperationType.ApplicationStarted); + Assert.IsTrue(request.Data.Equals("")); + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tests/SettingManagerTest.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tests/SettingManagerTest.cs new file mode 100755 index 0000000..d0cbe47 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tests/SettingManagerTest.cs @@ -0,0 +1,105 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRun.Models.Workout; +using System.Collections.Generic; +using MapMyRun.Models; +using MapMyRun.Models.Settings; + +namespace MapMyRun.Tests +{ + [TestFixture] + class SettingManagerTest + { + Mock mockDispatcher = null; + + [SetUp] + public void Setup() + { + mockDispatcher = new Mock(); + } + + [Test] + public void Initialize_Called_CallSendRequestWithGetSettings() + { + mockDispatcher.Setup(x => x.Listen(OperationType.GpsStateChanged)).Returns(new NotificationProvider(OperationType.GpsStateChanged)); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => { + if (arg.Operation == OperationType.GetSettings) + request = arg; + } + ); + + var settingManager = new SettingManager(mockDispatcher.Object); + settingManager.Initialize(null); + + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.AtLeastOnce()); + Assert.AreEqual(request.Operation, OperationType.GetSettings); + Assert.IsTrue(request.Data.Equals("")); + } + + [Test] + public void Initialize_Called_DontCallNullDelegate() + { + mockDispatcher.Setup(x => x.Listen(OperationType.GpsStateChanged)).Returns(new NotificationProvider(OperationType.GpsStateChanged)); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => { + if (arg.Operation == OperationType.GetSettings) + request = arg; + } + ); + + var settingManager = new SettingManager(mockDispatcher.Object); + settingManager.Initialize(null); + Assert.IsNotNull(request); + request.OnReceiveResponse(new Response("0", "", ResponseStatus.Success)); + } + + [Test] + public void Initialize_Called_CallDelegate() + { + mockDispatcher.Setup(x => x.Listen(OperationType.GpsStateChanged)).Returns(new NotificationProvider(OperationType.GpsStateChanged)); + Request request = null; + var isCalled = false; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => { + if (arg.Operation == OperationType.GetSettings) + request = arg; + } + ); + + var settingManager = new SettingManager(mockDispatcher.Object); + settingManager.Initialize(() => + { + isCalled = true; + }); + Assert.IsNotNull(request); + request.OnReceiveResponse(new Response("0", "", ResponseStatus.Success)); + Assert.IsTrue(isCalled); + } + + [Test] + public void Initialize_CalledObserver_SetCorrectState() + { + var notificationProvider = new NotificationProvider(OperationType.GpsStateChanged); + mockDispatcher.Setup(x => x.Listen(OperationType.GpsStateChanged)).Returns(notificationProvider); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => { + if (arg.Operation == OperationType.GetSettings) + request = arg; + } + ); + + var settingManager = new SettingManager(mockDispatcher.Object); + settingManager.Initialize(null); + Assert.IsTrue(settingManager.GpsState == GPSState.Off); + request.OnReceiveResponse(new Response("0", + "{\"gps\":{\"isEnabled\":true,\"isSupported\":true,\"systemValue\":true}}", ResponseStatus.Success)); + Assert.IsTrue(settingManager.GpsState == GPSState.Ready); + notificationProvider.PostNotification("1"); + Assert.IsTrue(settingManager.GpsState == GPSState.Acquired); + notificationProvider.PostNotification("0"); + Assert.IsTrue(settingManager.GpsState == GPSState.Acquiring); + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tests/WorkoutTest.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tests/WorkoutTest.cs new file mode 100755 index 0000000..b3ff9e0 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tests/WorkoutTest.cs @@ -0,0 +1,294 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRun.Models.Workout; +using System.Collections.Generic; +using MapMyRun.Models; +using MapMyRun.Models.Workout.Items; + +namespace MapMyRun.Tests +{ + [TestFixture] + class WorkoutTest + { + Mock mockDispatcher = null; + Mock mockPermissionManager = null; + + const string CaloriesKey = "calories"; + const string WillpowerKey = "willpower"; + const string HrZoneKey = "hrZone"; + const string IntensityKey = "intensity"; + const string AggregatesKey = "aggregates"; + const string CadenceKey = "cadence"; + const string HrKey = "hrData"; + const string SpeedKey = "speed"; + const string StrideLengthKey = "strideLength"; + const string FormCoachKey = "formCoach"; + const string GoalCoachKey = "speedCoach"; + const string SpeedCoachKey = "goalCoach"; + + [SetUp] + public void Setup() + { + mockDispatcher = new Mock(); + mockPermissionManager = new Mock(); + } + + [Test] + public void PrepareWorkout_Called_CallRequestPermissionWithSensor() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + mockPermissionManager.Setup(x => x.RequestPermission(PermissionType.Sensor, It.IsAny())); + + workout.PrepareWorkout(50, true, true, true); + + mockPermissionManager.Verify(x => x.RequestPermission(PermissionType.Sensor, It.IsAny()), Times.Once()); + } + + [Test] + public void PrepareWorkout_CalledWithGpsNeededTrue_CallRequestPermissionWithLocation() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + OnPermissionResponse onResponse = null; + mockPermissionManager.Setup(x => x.RequestPermission(PermissionType.Sensor, It.IsAny())) + .Callback((type, cb) => + { + onResponse = cb; + } + ); + mockPermissionManager.Setup(x => x.RequestPermission(PermissionType.Location, It.IsAny())); + + workout.PrepareWorkout(50, true, true, true); + onResponse(true, true); + + mockPermissionManager.Verify(x => x.RequestPermission(PermissionType.Sensor, It.IsAny()), Times.Once()); + mockPermissionManager.Verify(x => x.RequestPermission(PermissionType.Location, It.IsAny()), Times.Once()); + } + + [Test] + public void PrepareWorkout_CalledWithGpsNeededFalse_DontCallRequestPermissionWithLocation() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + OnPermissionResponse onResponse = null; + mockPermissionManager.Setup(x => x.RequestPermission(PermissionType.Sensor, It.IsAny())) + .Callback((type, cb) => + { + onResponse = cb; + } + ); + mockPermissionManager.Setup(x => x.RequestPermission(PermissionType.Location, It.IsAny())); + + workout.PrepareWorkout(50, false, true, true); + onResponse(true, true); + + mockPermissionManager.Verify(x => x.RequestPermission(PermissionType.Sensor, It.IsAny()), Times.Once()); + mockPermissionManager.Verify(x => x.RequestPermission(PermissionType.Location, It.IsAny()), Times.Never()); + } + + [Test] + public void PrepareWorkout_Called_CallDispatcherSendRequest() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + OnPermissionResponse onResponse = null; + mockPermissionManager.Setup(x => x.RequestPermission(PermissionType.Sensor, It.IsAny())) + .Callback((type, cb) => + { + onResponse = cb; + } + ); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => + { + request = arg; + } + ); + + workout.PrepareWorkout(50, false, true, true); + onResponse(false, false); + + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.Once()); + Assert.AreEqual(OperationType.PrepareWKO, request.Operation); + } + + [Test] + public void StartWorkout_Called_CallDispatcherSendRequest() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + OnPermissionResponse onResponse = null; + mockPermissionManager.Setup(x => x.RequestPermission(PermissionType.Sensor, It.IsAny())) + .Callback((type, cb) => + { + onResponse = cb; + } + ); + Request request = null; + mockDispatcher.Setup(x => x.SendRequest(It.IsAny())).Callback(arg => + { + request = arg; + } + ); + + workout.PrepareWorkout(50, false, true, true); + onResponse(false, false); + request.OnReceiveResponse(new Response("0", "banana", ResponseStatus.Success)); + workout.StartWorkout(); + + mockDispatcher.Verify(x => x.SendRequest(It.IsAny()), Times.Exactly(2)); + Assert.AreEqual(OperationType.StartWKO, request.Operation); + } + + [Test] + public void StartListenWorkoutData_Called_CallDispatcherListen() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + mockDispatcher.Setup(x => x.Listen(OperationType.WkoDataChanged)).Returns(new NotificationProvider(OperationType.WkoDataChanged)); + + workout.StartListenWorkoutData(); + + mockDispatcher.Verify(x => x.Listen(OperationType.WkoDataChanged), Times.Once()); + } + + [Test] + public void PropagateWorkoutData_Called_CallCorrectEvent() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + mockDispatcher.Setup(x => x.Listen(OperationType.WkoDataChanged)).Returns(new NotificationProvider(OperationType.WkoDataChanged)); + + var calledEvents = new List(); + workout.CaloriesDataReceived += (x) => { calledEvents.Add(CaloriesKey); }; + workout.WillpowerDataReceived += (x) => { calledEvents.Add(WillpowerKey); }; + workout.HrZoneDataReceived += (x) => { calledEvents.Add(HrZoneKey); }; + workout.IntensityDataReceived += (x) => { calledEvents.Add(IntensityKey); }; + workout.AggregatesDataReceived += (x) => { calledEvents.Add(AggregatesKey); }; + workout.CadenceDataReceived += (x) => { calledEvents.Add(CadenceKey); }; + workout.HrDataReceived += (x) => { calledEvents.Add(HrKey); }; + workout.SpeedDataReceived += (x) => { calledEvents.Add(SpeedKey); }; + workout.StrideLengthDataReceived += (x) => { calledEvents.Add(StrideLengthKey); }; + workout.FormCoachDataReceived += (x) => { calledEvents.Add(FormCoachKey); }; + workout.GoalCoachDataReceived += (x) => { calledEvents.Add(GoalCoachKey); }; + workout.SpeedCoachDataReceived += (x) => { calledEvents.Add(SpeedCoachKey); }; + + Dictionary workEventTestData = new Dictionary() + { + { CaloriesKey, "{\"calories\":198}" }, + { WillpowerKey, "{\"willpower\": \"willpower\"}" }, + { HrZoneKey, "{\"hrZone\": \"banana\"}" }, + { IntensityKey, "{\"intensity\": \"orange\"}" }, + { AggregatesKey, "{\"aggregates\": { \"distance_total\":0,\"metabolic_energy_total\":0 } }" }, + { CadenceKey, "{\"cadence\": { \"min\":0,\"max\":0 } }" }, + { HrKey, "{\"hrData\": { \"min\":0,\"max\":0 } }" }, + { SpeedKey, "{\"speed\": { \"min\":0,\"max\":0 } }" }, + { StrideLengthKey, "{\"strideLength\": { \"min\":0,\"max\":0 } }" }, + { FormCoachKey, "{\"formCoach\": { \"targetMin\":0,\"targetMax\":0 } }" }, + { GoalCoachKey, "{\"goalCoach\": { \"targetMin\":0,\"targetMax\":0 } }" }, + { SpeedCoachKey, "{\"speedCoach\": { \"current\":0,\"target\":0 } }" } + }; + + foreach (var entry in workEventTestData) + { + Console.WriteLine($"Key {entry.Key}"); + calledEvents.Clear(); + WorkoutData workoutData = workout.ParseWorkoutDataJson(entry.Value); + workout.PropagateWorkoutData(workoutData); + Assert.AreEqual(1, calledEvents.Count); + Assert.AreEqual(entry.Key, calledEvents[0]); + } + } + + [Test] + public void PropagateWorkoutData_Called_CallMultiCorrectEvent() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + mockDispatcher.Setup(x => x.Listen(OperationType.WkoDataChanged)).Returns(new NotificationProvider(OperationType.WkoDataChanged)); + + var calledEvents = new List(); + workout.CaloriesDataReceived += (x) => { calledEvents.Add(CaloriesKey); }; + workout.WillpowerDataReceived += (x) => { calledEvents.Add(WillpowerKey); }; + workout.HrZoneDataReceived += (x) => { calledEvents.Add(HrZoneKey); }; + workout.IntensityDataReceived += (x) => { calledEvents.Add(IntensityKey); }; + workout.AggregatesDataReceived += (x) => { calledEvents.Add(AggregatesKey); }; + workout.CadenceDataReceived += (x) => { calledEvents.Add(CadenceKey); }; + workout.HrDataReceived += (x) => { calledEvents.Add(HrKey); }; + workout.SpeedDataReceived += (x) => { calledEvents.Add(SpeedKey); }; + workout.StrideLengthDataReceived += (x) => { calledEvents.Add(StrideLengthKey); }; + workout.FormCoachDataReceived += (x) => { calledEvents.Add(FormCoachKey); }; + workout.GoalCoachDataReceived += (x) => { calledEvents.Add(GoalCoachKey); }; + workout.SpeedCoachDataReceived += (x) => { calledEvents.Add(SpeedCoachKey); }; + + var calorieWillpowerIntensity = "{\"calories\":198, \"willpower\": \"willpower\", \"intensity\": \"orange\"}"; + var cadenceSpeedcoachGoal = "{\"cadence\": { \"min\":0,\"max\":0 }, \"speedCoach\": { \"current\":0,\"target\":0 }, \"goalCoach\": { \"targetMin\":0,\"targetMax\":0 } }"; + var aggregateSpeedHr = "{\"aggregates\": { \"distance_total\":0,\"metabolic_energy_total\":0 }, \"hrData\": { \"min\":0,\"max\":0 }, \"speed\": { \"min\":0,\"max\":0 } }"; + var hrZoneStrideForm = "{\"hrZone\": \"banana\", \"strideLength\": { \"min\":0,\"max\":0 }, \"formCoach\": { \"targetMin\":0,\"targetMax\":0 } }"; + + calledEvents.Clear(); + WorkoutData workoutData = workout.ParseWorkoutDataJson(calorieWillpowerIntensity); + workout.PropagateWorkoutData(workoutData); + Assert.AreEqual(3, calledEvents.Count); + Assert.AreEqual(CaloriesKey, calledEvents[0]); + Assert.AreEqual(WillpowerKey, calledEvents[1]); + Assert.AreEqual(IntensityKey, calledEvents[2]); + + calledEvents.Clear(); + workoutData = workout.ParseWorkoutDataJson(cadenceSpeedcoachGoal); + workout.PropagateWorkoutData(workoutData); + Assert.AreEqual(3, calledEvents.Count); + Assert.AreEqual(CadenceKey, calledEvents[0]); + Assert.AreEqual(GoalCoachKey, calledEvents[1]); + Assert.AreEqual(SpeedCoachKey, calledEvents[2]); + + calledEvents.Clear(); + workoutData = workout.ParseWorkoutDataJson(aggregateSpeedHr); + workout.PropagateWorkoutData(workoutData); + Assert.AreEqual(3, calledEvents.Count); + Assert.AreEqual(HrKey, calledEvents[0]); + Assert.AreEqual(AggregatesKey, calledEvents[1]); + Assert.AreEqual(SpeedKey, calledEvents[2]); + + calledEvents.Clear(); + workoutData = workout.ParseWorkoutDataJson(hrZoneStrideForm); + workout.PropagateWorkoutData(workoutData); + Assert.AreEqual(3, calledEvents.Count); + Assert.AreEqual(HrZoneKey, calledEvents[0]); + Assert.AreEqual(StrideLengthKey, calledEvents[1]); + Assert.AreEqual(FormCoachKey, calledEvents[2]); + } + + [Test] + public void AddItem_Called_AddedCorrectItem() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + + workout.AddItem(WorkoutItemType.Calories); + var result = workout.GetItem(WorkoutItemType.Calories); + + Assert.IsNotNull(result); + } + + [Test] + public void GetItem_Called_ReturnNull() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + + var result = workout.GetItem(WorkoutItemType.Calories); + + Assert.IsNull(result); + } + + [Test] + public void ClearAllWorkoutItem_Called_ClearAllItems() + { + var workout = new Workout(mockDispatcher.Object, mockPermissionManager.Object); + + workout.AddItem(WorkoutItemType.Calories); + workout.AddItem(WorkoutItemType.PeakHR); + Assert.IsNotNull(workout.GetItem(WorkoutItemType.Calories)); + Assert.IsNotNull(workout.GetItem(WorkoutItemType.PeakHR)); + + workout.ClearAllWorkoutItem(); + + Assert.IsNull(workout.GetItem(WorkoutItemType.Calories)); + Assert.IsNull(workout.GetItem(WorkoutItemType.PeakHR)); + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml new file mode 100755 index 0000000..a9d222b --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml @@ -0,0 +1,46 @@ + + + + + + + #F8F8F9 + #F8F8F9 + + #F8F8F9 + #008DFF + + + + + + + + + + \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml.cs new file mode 100755 index 0000000..ec210e9 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/App.xaml.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xamarin.Forms; +using Xamarin.Forms.Xaml; +using MapMyRun.Models; +using MapMyRun.Tizen.Models; +using MapMyRun.Tizen.Views; +using MapMyRun.Models.Workout; +using MapMyRun.Models.Settings; + +namespace MapMyRun.Tizen +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class App : Application + { + private const string ServiceAppId = "org.tizen.example.MapMyRunService"; + //private const string ServiceAppId = "org.tizen.mapmyrunservice"; + private readonly Loading loading; + public Workout workout; + public SettingManager settingManager; + public App() + { + InitializeComponent(); + var dispatcher = MapMyRun.Models.Dispatcher.Instance; + settingManager = new SettingManager(dispatcher); + loading = new Loading(new AppLauncher(), new MessagePortHandler(), + dispatcher, new PhoneService(dispatcher, new TizenPlatformService()), settingManager, ServiceAppId); + MainPage = new LoadingPage(loading); + workout = new Workout(dispatcher, new PermissionManager(dispatcher)); + workout.StartListenWorkoutData(); + } + + protected override void OnStart() + { + Console.WriteLine("OnStart!========================"); + loading.StartLoading(); + } + + protected override void OnSleep() + { + Console.WriteLine("OnSleep!========================"); + // Handle when your app sleeps + } + + protected override void OnResume() + { + Console.WriteLine("OnResume!========================"); + // Handle when your app resumes + } + } +} + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.Tizen.csproj b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.Tizen.csproj new file mode 100755 index 0000000..89b3a9d --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.Tizen.csproj @@ -0,0 +1,26 @@ + + + + Exe + tizen40 + + + + portable + + + None + + + + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.cs new file mode 100755 index 0000000..d259400 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/MapMyRun.cs @@ -0,0 +1,23 @@ +using System; +using Xamarin.Forms; + +namespace MapMyRun.Tizen +{ + class Program : global::Xamarin.Forms.Platform.Tizen.FormsApplication + { + protected override void OnCreate() + { + base.OnCreate(); + + LoadApplication(new App()); + } + + static void Main(string[] args) + { + var app = new Program(); + Forms.Init(app); + global::Tizen.Wearable.CircularUI.Forms.Renderer.FormsCircularUI.Init(); + app.Run(args); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/AppLauncher.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/AppLauncher.cs new file mode 100755 index 0000000..2fd9e0e --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/AppLauncher.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Xamarin.Forms; +using Tizen.Applications; +using MapMyRun.Models; +using MapMyRun.Tizen.Models; + +namespace MapMyRun.Tizen.Models +{ + public class AppLauncher : IAppLauncher + { + public void Launch(string appId) + { + var control = new AppControl() + { + ApplicationId = appId + }; + + AppControl.SendLaunchRequest(control); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/MessagePortHandler.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/MessagePortHandler.cs new file mode 100755 index 0000000..9792b10 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/MessagePortHandler.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Tizen.Applications; +using Tizen.Applications.Messages; +using MapMyRun.Models; +using Xamarin.Forms; + +namespace MapMyRun.Tizen.Models +{ + public class MessagePortHandler : IMessagePortHandler + { + private string _localPortName = ""; + private string _remotePortName = ""; + private string _remoteAppId = ""; + private bool _isSecureMode = false; + + private bool _isInit = false; + private bool _isPendingMessageSending = false; + private List> _pendingMessages = new List>(); + + private MessagePort messagePort; + + public event Action> MessageReceived = delegate { }; + + public void Connect(string localPort, string remoteAppId, string remotePort, bool secureMode) + { + _localPortName = localPort; + _remotePortName = remotePort; + _remoteAppId = remoteAppId; + _isSecureMode = secureMode; + + messagePort = new MessagePort(_localPortName, _isSecureMode); + messagePort.MessageReceived += OnReceive; + messagePort.Listen(); + } + + public void Disconnect() + { + _localPortName = ""; + _remotePortName = ""; + _remoteAppId = ""; + _isSecureMode = false; + messagePort.StopListening(); + messagePort.Dispose(); + messagePort = null; + } + + public void Send(Dictionary data) + { + if (!_isInit) + { + _pendingMessages.Add(data); + + if (!_isPendingMessageSending) + { + _isPendingMessageSending = true; + TrySendFirstMessage(); + } + return; + } + + SendMessage(data); + } + + private void TrySendFirstMessage() + { + try + { + var remotePort = new RemotePort(_remoteAppId, _remotePortName, false); + bool isRunning = remotePort.IsRunning(); + Console.WriteLine($"Remote Port: {_remoteAppId}, {_remotePortName}, {isRunning}"); + + if (!isRunning) + { + StartRetrySendFirstMessage(); + return; + } + + foreach (Dictionary pendingMessage in _pendingMessages) + { + //TODO : Fail case, Exit? or Recovery? + SendMessage(pendingMessage); + } + + _isPendingMessageSending = false; + _isInit = true; + } + catch (Exception ex) + { + Console.WriteLine("Exception - Message: " + ex.Message + ", source: " + ex.Source + ", " + ex.StackTrace); + StartRetrySendFirstMessage(); + } + } + + void SendMessage(Dictionary data) + { + var bundle = new Bundle(); + Console.WriteLine($"________________________________"); + Console.WriteLine($">>>>> [UI] Send Data: "); + foreach (var pair in data) + { + bundle.AddItem(pair.Key, pair.Value); + Console.WriteLine($"{{ Key: {pair.Key}, Value: {pair.Value} }}"); + } + + try + { + messagePort.Send(bundle, _remoteAppId, _remotePortName, _isSecureMode); + Console.WriteLine($"Message sent: {_remoteAppId}, {_remotePortName}, {_isSecureMode}"); + } + catch (Exception ex) + { + Console.WriteLine("Exception - Message: " + ex.Message + ", source: " + ex.Source + ", " + ex.StackTrace); + } + } + + private void StartRetrySendFirstMessage() + { + Device.StartTimer(TimeSpan.FromSeconds(0.3), () => + { + TrySendFirstMessage(); + return false; + }); + } + + private void OnReceive(object sender, MessageReceivedEventArgs e) + { + StringBuilder messageLog = new StringBuilder(); + + Console.WriteLine($"________________________________"); + Console.WriteLine("UI application received a message"); + Console.WriteLine($"App ID: {e.Remote.AppId}"); + Console.WriteLine($"PortName: {e.Remote.PortName}"); + Console.WriteLine($"Trusted: {e.Remote.Trusted}"); + Bundle responseBundle = e.Message; + + Console.WriteLine($"[UI] Response Data <<<<< : "); + var reponse = new Dictionary(); + + foreach (string key in responseBundle.Keys) + { + if (responseBundle.TryGetItem(key, out string value)) + { + Console.WriteLine($"{{ Key: {key}, Value: {value} }}"); + reponse.Add(key, value); + } + } + + MessageReceived(reponse); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/TizenPlatformService.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/TizenPlatformService.cs new file mode 100755 index 0000000..071f2a6 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/Models/TizenPlatformService.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MapMyRun.Models; + +namespace MapMyRun.Tizen.Models +{ + class TizenPlatformService : IPlatformService + { + public void GetSwVersion(out string version) + { + global::Tizen.System.Information.TryGetValue("http://tizen.org/system/build.string", out version); + Console.WriteLine($"SW Version {version}"); + } + + public void KeepScreenOn() + { + // TODO : Check it API Version 5 + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/BasePageModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/BasePageModel.cs new file mode 100755 index 0000000..7cc2e50 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/BasePageModel.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace MapMyRun.Tizen.ViewModels +{ + public class BasePageModel : INotifyPropertyChanged + { + protected bool SetProperty(ref T backingStore, T value, + [CallerMemberName]string propertyName = "", + Action onChanged = null) + { + if (EqualityComparer.Default.Equals(backingStore, value)) + { + return false; + } + + backingStore = value; + onChanged?.Invoke(); + OnPropertyChanged(propertyName); + return true; + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/LoadingPageModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/LoadingPageModel.cs new file mode 100755 index 0000000..d26cf4d --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/LoadingPageModel.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using MapMyRun.Models; +namespace MapMyRun.Tizen.ViewModels +{ + class LoadingPageModel : BasePageModel + { + private double _progress; + private Loading _loadingModel; + + public double Progress + { + get + { + return _progress; + } + + set + { + if (_progress >= 1) + return; + + double changedValue = value; + if (value >= 1) + { + changedValue = 1; + } + SetProperty(ref _progress, changedValue, "Progress"); + } + } + + public LoadingPageModel(Loading model) + { + _loadingModel = model; + _loadingModel.LoadingStateChanged += (state) => + { + Progress = (double)state / Enum.GetValues(typeof(LoadingState)).Length; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/MainPageModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/MainPageModel.cs new file mode 100755 index 0000000..d66ef55 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/MainPageModel.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using MapMyRun.Models.Settings; +using Xamarin.Forms; +using MapMyRun.Models; + +namespace MapMyRun.Tizen.ViewModels +{ + public class MainPageModel : BasePageModel + { + private SettingManager settingManager; + + private GPSState _gpsState; + public GPSState GpsState + { + get + { + return _gpsState; + } + set + { + SetProperty(ref _gpsState, value, "GpsState"); + + switch (value) + { + case GPSState.Acquired: + GpsStatusImg = ImageSource.FromFile("icon-gps--connected.png"); + break; + case GPSState.Ready: + case GPSState.Acquiring: + GpsStatusImg = ImageSource.FromFile("icon-gps--connecting.png"); + break; + case GPSState.DisabledForActivity: + GpsStatusImg = ImageSource.FromFile("icon-gps--not-connected.png"); + break; + case GPSState.Off: + GpsStatusImg = ImageSource.FromFile("icon-gps--not-connected.png"); + break; + default: + GpsStatusImg = ImageSource.FromFile("icon-gps--error.png"); + break; + } + } + } + + private ImageSource _gpsStatusImg; + public ImageSource GpsStatusImg + { + get + { + return _gpsStatusImg; + } + set + { + SetProperty(ref _gpsStatusImg, value, "GpsStatusImg"); + } + } + + public MainPageModel() + { + settingManager = ((App)Application.Current).settingManager; + settingManager.GpsStateChanged += (GPSState nextGpsState) => + { + GpsState = nextGpsState; + }; + GpsState = settingManager.GpsState; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/SettingsPageModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/SettingsPageModel.cs new file mode 100755 index 0000000..37df409 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/SettingsPageModel.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using Xamarin.Forms; +using MapMyRun.Models.Settings; + +namespace MapMyRun.Tizen.ViewModels +{ + public class SettingsPageModel : BasePageModel + { + private SettingManager settingManager; + private DisplayTimeType _displayTime { get; set; } + + public int DisplayTime { + get { + return (int) _displayTime; + } + set { + if (Enum.IsDefined(typeof(DisplayTimeType), value)) + { + _displayTime = (DisplayTimeType)value; + } + } + } + + + private bool gpsStatus; + public bool GPSStatus + { + get + { + return gpsStatus; + } + set + { + gpsStatus = value; + settingManager.ToggleGPS(gpsStatus); + } + } + + private static SettingsPageModel _instance; + + public static SettingsPageModel GetInstance() + { + if (_instance == null) + { + _instance = new SettingsPageModel(); + } + return _instance; + } + + private SettingsPageModel() + { + // Insert loading of settings here. + settingManager = ((App)Application.Current).settingManager; + _displayTime = DisplayTimeType.longDisplayTime; + GPSStatus = false; + } + } + + public enum DisplayTimeType + { + longDisplayTime = 16, + midDisplayTime = 12, + shortDisplayTime = 6 + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/AverageHrViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/AverageHrViewModel.cs new file mode 100755 index 0000000..23bf14b --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/AverageHrViewModel.cs @@ -0,0 +1,72 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class AverageHrViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + string nextValue = String.Equals(value, "0", StringComparison.OrdinalIgnoreCase) ? Item.GetEmptyValue() : value; + + SetProperty(ref _val, nextValue); + ValueWithUnit = nextValue; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public AverageHrViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.AvgHR; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("AverageHr Initialize!"); + + var item = (AverageHr)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"AverageHr Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CadenceViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CadenceViewModel.cs new file mode 100755 index 0000000..251cc56 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CadenceViewModel.cs @@ -0,0 +1,70 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class CadenceViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + SetProperty(ref _val, value); + ValueWithUnit = value; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public CadenceViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.Cadence; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("CurrentCadence Initialize!"); + + var item = (CurrentCadence)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"CurrentCadence Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CaloriesViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CaloriesViewModel.cs new file mode 100755 index 0000000..a2450bc --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CaloriesViewModel.cs @@ -0,0 +1,70 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class CaloriesViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + SetProperty(ref _val, value); + ValueWithUnit = value; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public CaloriesViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.Calories; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("Calorie Initialize!"); + + var item = (Calories)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"Calories Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentHrViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentHrViewModel.cs new file mode 100755 index 0000000..48c9e52 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentHrViewModel.cs @@ -0,0 +1,72 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class CurrentHrViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + string nextValue = String.Equals(value, "0", StringComparison.OrdinalIgnoreCase) ? Item.GetEmptyValue() : value; + + SetProperty(ref _val, nextValue); + ValueWithUnit = nextValue; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public CurrentHrViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.CurrentHR; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("CurrentHr Initialize!"); + + var item = (CurrentHr)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"CurrentHr Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentPaceViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentPaceViewModel.cs new file mode 100755 index 0000000..8648739 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/CurrentPaceViewModel.cs @@ -0,0 +1,70 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class CurrentPaceViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + SetProperty(ref _val, value); + ValueWithUnit = value; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public CurrentPaceViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.Pace; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("CurrentPace Initialize!"); + + var item = (CurrentPace)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"CurrentPace Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DistanceViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DistanceViewModel.cs new file mode 100755 index 0000000..76921f9 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DistanceViewModel.cs @@ -0,0 +1,70 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class DistanceViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + SetProperty(ref _val, value); + ValueWithUnit = value; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public DistanceViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.Distance; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("Distance Initialize!"); + + var item = (Distance)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"Distance Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DurationViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DurationViewModel.cs new file mode 100755 index 0000000..02367e1 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/DurationViewModel.cs @@ -0,0 +1,98 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class DurationViewModel : BasePageModel, IWorkoutItemViewModel + { + + private string _val; + private string _valWithUnit; + private int _seconds; + private Timer secondsTimer; + + public string Value + { + get { return _val; } + set + { + SetProperty(ref _val, value); + ValueWithUnit = value; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public DurationViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.Duration; + Item = item; + Value = item.GetEmptyValue(); + } + + public int Seconds + { + get { return _seconds; } + set + { + _seconds = value; + int h = _seconds / 3600; + int m = (_seconds - h * 3600) / 60; + int s = _seconds - h * 3600 - m * 60; + Value = (h == 0 ? "" : $"{h}:") + (h > 0 && m < 10 ? "0" : "") + $"{m}:" + (s < 10 ? "0" : "") + $"{s}"; + } + } + + public void Initialize() + { + Console.WriteLine("Duration Initialize!"); + + secondsTimer = new System.Timers.Timer(1000); + secondsTimer.Elapsed += (s, e) => + { + Seconds++; + }; + secondsTimer.AutoReset = true; + secondsTimer.Enabled = true; + } + + public void StopTimer() + { + secondsTimer.Enabled = false; + } + + public void ResumeTimer() + { + secondsTimer.Enabled = true; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/IWorkoutItemViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/IWorkoutItemViewModel.cs new file mode 100755 index 0000000..3de9653 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/IWorkoutItemViewModel.cs @@ -0,0 +1,20 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + public interface IWorkoutItemViewModel + { + string Value { get; set; } + string ValueWithUnit { get; } + string LongLabel { get; } + string MediumLabel { get; } + string ShortLabel { get; } + WorkoutItemType ItemType { get; set; } + IWorkoutItem Item { get; set; } + void Initialize(); + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakHrViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakHrViewModel.cs new file mode 100755 index 0000000..5ffe5b0 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakHrViewModel.cs @@ -0,0 +1,72 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class PeakHrViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + string nextValue = String.Equals(value, "0", StringComparison.OrdinalIgnoreCase) ? Item.GetEmptyValue() : value; + + SetProperty(ref _val, nextValue); + ValueWithUnit = nextValue; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public PeakHrViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.PeakHR; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("PeakHR Initialize!"); + + var item = (PeakHr)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"PeakHR Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakPaceViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakPaceViewModel.cs new file mode 100755 index 0000000..b7ecb74 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/PeakPaceViewModel.cs @@ -0,0 +1,70 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class PeakPaceViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + SetProperty(ref _val, value); + ValueWithUnit = value; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public PeakPaceViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.MaxPace; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("MaxPace Initialize!"); + + var item = (MaxPace)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"MaxPace Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/WorkoutMainPageModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/WorkoutMainPageModel.cs new file mode 100755 index 0000000..dd2866c --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/WorkoutMainPageModel.cs @@ -0,0 +1,97 @@ +using MapMyRun.Models.Workout; +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class WorkoutMainPageModel : BasePageModel + { + private readonly Workout _workout; + Dictionary _workoutItemViewModels = new Dictionary(); + + public WorkoutMainPageModel(Workout workout) + { + _workout = workout; + } + + public IWorkoutItemViewModel GetWorkoutViewModel(int page, int number) + { + //TODO : Model should provide valid workitem list per each activity and check here. + WorkoutItemType[,] workoutItemSet = new WorkoutItemType[,] { + {WorkoutItemType.Duration, WorkoutItemType.Distance, WorkoutItemType.Calories, WorkoutItemType.CurrentHR }, + {WorkoutItemType.Pace, WorkoutItemType.MaxPace, WorkoutItemType.Cadence, WorkoutItemType.CurrentHR}, + {WorkoutItemType.PeakHR, WorkoutItemType.AvgHR, WorkoutItemType.CurrentHR, WorkoutItemType.HRZone } + }; + + if (page > workoutItemSet.GetUpperBound(0)) + return null; + + if (number > workoutItemSet.GetUpperBound(1)) + return null; + + var type = workoutItemSet[page, number]; + var itemViewModel = GetItemViewModel(type); + + return itemViewModel; + } + + IWorkoutItemViewModel GetItemViewModel(WorkoutItemType type) + { + if (_workoutItemViewModels.TryGetValue((int)type, out IWorkoutItemViewModel item)) + return item; + + var itemViewModel = CreateViewModel(_workout.GetItem(type)); + if (itemViewModel == null) + return null; + + itemViewModel.Initialize(); + _workoutItemViewModels.Add((int)type, itemViewModel); + return itemViewModel; + } + + IWorkoutItemViewModel CreateViewModel(IWorkoutItem item) + { + IWorkoutItemViewModel ret = null; + + switch (item.GetWorkoutItemType()) + { + case WorkoutItemType.Duration: + ret = new DurationViewModel(item); + break; + case WorkoutItemType.Calories: + ret = new CaloriesViewModel(item); + break; + case WorkoutItemType.Distance: + ret = new DistanceViewModel(item); + break; + case WorkoutItemType.CurrentHR: + ret = new CurrentHrViewModel(item); + break; + case WorkoutItemType.PeakHR: + ret = new PeakHrViewModel(item); + break; + case WorkoutItemType.AvgHR: + ret = new AverageHrViewModel(item); + break; + case WorkoutItemType.HRZone: + ret = new ZoneHrViewModel(item); + break; + case WorkoutItemType.Pace: + ret = new CurrentPaceViewModel(item); + break; + case WorkoutItemType.MaxPace: + ret = new PeakPaceViewModel(item); + break; + case WorkoutItemType.Cadence: + ret = new CadenceViewModel(item); + break; + default: + break; + } + return ret; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/ZoneHrViewModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/ZoneHrViewModel.cs new file mode 100755 index 0000000..02082e1 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutMain/ZoneHrViewModel.cs @@ -0,0 +1,70 @@ +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace MapMyRun.Tizen.ViewModels.WorkoutMain +{ + class ZoneHrViewModel : BasePageModel, IWorkoutItemViewModel + { + private string _val; + private string _valWithUnit; + public string Value + { + get { return _val; } + set + { + SetProperty(ref _val, value); + ValueWithUnit = value; + } + } + + public string ValueWithUnit + { + get { return _valWithUnit; } + set + { + SetProperty(ref _valWithUnit, value + Item.GetUnit()); + } + } + + public string LongLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.LongLabel); } + } + + public string MediumLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.MediumLabel); } + } + + public string ShortLabel + { + get { return Item.GetLabel(WorkoutItemLabelType.ShortLabel); } + } + + public WorkoutItemType ItemType { get; set; } + public IWorkoutItem Item { get; set; } + + public ZoneHrViewModel(IWorkoutItem item) + { + ItemType = WorkoutItemType.HRZone; + Item = item; + Value = item.GetEmptyValue(); + } + + public void Initialize() + { + Console.WriteLine("ZoneHr Initialize!"); + + var item = (ZoneHr)Item; + item.ValueChanged += (x) => + { + Console.WriteLine($"ZoneHr Changed : {x}"); + Value = x; + }; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutSetupPageModel.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutSetupPageModel.cs new file mode 100755 index 0000000..60f1c37 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/ViewModels/WorkoutSetupPageModel.cs @@ -0,0 +1,72 @@ +using MapMyRun.Models; +using MapMyRun.Tizen.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Tizen.Applications; + +namespace MapMyRun.Tizen.ViewModels +{ + public class WorkoutSetupPageModel : BasePageModel + { + private ActivityRepository activityRepository; + + public List defaultList { get; set; } + + private string _currentActivity; + public string currentActivity { + get + { + return _currentActivity; + } + + set + { + SetProperty(ref _currentActivity, value, "currentActivity"); + } + } + + public WorkoutSetupPageModel() + { + string fileLocation = Application.Current.DirectoryInfo.Resource + $"localization/en.json"; + string json; + + using (StreamReader reader = new StreamReader(fileLocation)) + { + json = reader.ReadToEnd(); + } + + activityRepository = new ActivityRepository(json); + + GetPopularList(); + + currentActivity = activityRepository.popularActivityList.Find((Activity activity) => + { + return activity.id == 16; // Default set to Run for now + })?.name; + } + + public void GetPopularList() + { + defaultList = activityRepository.popularActivityList; + defaultList.Add(new Activity() { name = "All Activities", group_id = -1 }); + } + + public List GetGroupActivities() + { + return activityRepository.activityGroupings; + } + + public List GetActivitiesByGroup(int group_id) + { + return activityRepository.GetActivitiesByGroupId(group_id); + } + + public void updateCurrentActivity(Activity activity) + { + currentActivity = activity.name; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/BPMIcon.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/BPMIcon.png new file mode 100755 index 0000000000000000000000000000000000000000..8c87d9a3ef3022fa95d953e680398ae0b6573bc5 GIT binary patch literal 682 zcmV;b0#*HqP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0MkiCK~zXf?Ud0D zgCGn=%l`kL+3rFKR-m__?qw%28f|-8QBuxX45u4~RL&k${;QmoQ4*LvQ@s58ycM&B zM{!I(_{OQ2O;T)pG+(gB;iN0DYy230@nif{%1M1r)WwHZ-fP5C8{7T3AVM)ldccZnM*$#Yq=(wQiC5$vr<$WzeTV$OY>lsyckAqa zTpq*bJuwnjC!glsHL=?)D~?P~#YHFfin7fuz3NeMS_)RzxRu~KBoz^P1JP3^F<|no z{r~^~07*qoM6N<$f^=Vtyw~fY`8o!J0g+d$Rdl7XUa!&mcRoKq=_hWvIJH`h{uKDz z>@V;==-_NNi=q2eDiuhjQq+EXdxKW1MeSQAlL^dbGp1In6_m?mI#_m|?sPhdUau#I z!-03N;I2!ONF+!um%|7lYPFh7r&D4!o2kFqY>3O{BGG7+?Du;b6T%;2jYdOCr4mL? zgTd`~<9C)0Uw4LI1u-(246Q+33@8?hm?9mON(EZ27QOrM|5CzPoUR<`_xqSC4Q{z% zxm==mpW8W?Jr;7x`+q2v%3o)Pd2J~a3bg)?!{J~aSe~mTIEy>2r#1Qm;c%GQ8^sJ# z+3j|0@jUT(oY_+#X7D15#e%J*lT0R=y>7$|UMUz1VoP!I`8=}+L(JeWNqH~x`FzaU zJ7NZzdh}BF{{GIac_3!+p|;yCjyqwq*_e4EVg^OkZnx=;#a;f)=Z{6;2lUDq5UYv^ QtN;K207*qoM6N<$g0hD-+5i9m literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/GPSIcon.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/GPSIcon.png new file mode 100755 index 0000000000000000000000000000000000000000..753df411c209896fb8666e3de7cdbf918d77eec4 GIT binary patch literal 988 zcmV<210(#2P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0`*BmK~z{r#hATn zGC>rE$Grx@!Zv6lX|%Ag5L++6CTbJ3h)qZ#VrwH7b^$?~poN8PkOZvMBtj4(3VQ)w z$7YT>yWjpCXJ*y)d*Jmwd)%2bx;s0gA;U13A4;W?kw_$1DwSfnTn@8;=(^4}n+;nm z7OdOthA^!T3`VA6v1q)%zw^*PtyT++?AI+aY1hxs54ZDax7%Q34O3(i^ziV&EfC0h zATs&-`pPXh;IeZ(bv=pMY{o5k?(Xiu9JL*@so(D#_xJaBBxgRKv(L{@w%_krG#X{; zbed%{8GKg`pPB6C<>lY3Ge?DAof3sY0nG8K?85g|vD(=?k+NE?!0c^=!t;gj=MvcA z{KL!B(-S0w@Jj;bD5fH8C&av=%|sl9`FtM7CZ<-afl;4WC~POCQmI&tl&Ds#V2)xU;rXigS||_T zPS8=5(g{08^(}-OC} zkB=Dfbr42m!*OsWtd-cYHo;GaYtn2sG5W*vFNCh^Mm!$Jqrh?OPduwOAIN1kFcSC^ z7b6?yoov@&FyIzAp1PidO@U!PSKRU>OksnIGaL@V$eLu*)O0%Kc5%pBATsIF#W;3( zOp!@KXVd9)z^J}0kx94e^*Xn+A^WzAOj6=M=D3{<*{54%QXt>R?i0Jnq(sV;%VjXC z4^m`OB-Q87Xfy&N`#2^tDU)nK_(wL`RwOd1BAlwnAlt}9CRH_=Ot^U<&s7nb^rNq@ zFK!m(IjSO)ezjOExY6&a%V58>XURx+NWl;Q00002_4Rc&Um+Xxb#QPHEqUZWvbj7B*`QK+s&c%&y|L>x zWrJF7Zf;@=zi{gU$Og4NJw3%X!aoL^vcW(mlSxrrT+ANRm&8AB0iteCRdne90000< KMNUMnLSTZ%naU3U literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/UAIcon.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/UAIcon.png new file mode 100755 index 0000000000000000000000000000000000000000..57d7e206932d5ba3f4e7c10984b818da41967251 GIT binary patch literal 3552 zcmV<64IlD}P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf4RuLGK~#8N?VWc_ z6-yL{N9@>w#*X^z-C#xRq7kqw_7Vj(Mx*{Airqw_Bt~P4HNhA$Vyr0k-m&-Id%=PY zuz>E{GaL|PxZJ(lW-s$4zvs+c-8;MQ+}SB-&g3A3NV2#hZ{ED5YuB#C%gc+DC{cp+ z>eY*urH~^BR9m10MT!(5pFe-bTry|QoFVt`-zVF)Z6hB(d?3-$(fEnEfT5Zu*#MFU+1_!yMYy?b}&b%m6xaN)xERF(1O%^PXbq)Ae}di9vs)j`ZcO(nmo zX)72W9*(!E4qsni=JyCGSFvKn@Tscf;K75E++AaShel!+s#epcO{F<==1A|~zsFnA zj`i!;Grv?wvct~MkVOU-Lh>D70e!q}%jT<)( zpQ=8bK7CpmFkk@lyA_IAXt(_R{iP#Ej$pNnVf*&&%(@6Em)?e&7U=qUdU`VJk(FHJ z|0o8gjn17rN96A&TefT=ZQHiRPXtyYPo6xO(x1VD2a~gB&ys|M1QHk+h^6+!wBUaQ zzXYuYLuwH%s&;}=yO}ImvV>VLhM0v^Fe02kf4=nf>sP!i9|%L$P6Z1V#HVU5Cr_Sa z)-OfOSHGX1AGvq$9$l4?M{lH1p+fixAN>0D3sVBMM~@y&HgC>>7CxokLh9t`gumXbTemJC{4S(c=5t=Mk>$_7>VTOB>QIz9qAf~6ZfwdKdM^Z=q0RaJ+5@aX-{P}|^et|ls z+uWBjXJrKn6evKwa0KcoU%otf_Uswv_#`4CBK(;F?#Xq%c*T$4U4gLD{!xX<< zy?Qmhp`JK#f-YNGfuy7)ObOIrg$e)t`}b#Dnez4Z#gsr`KG1MpzkWTVp`Jf~p7sd@ z*#q7u{Fg6ZPXA0dm`V%0T6ytRWYM5O19Iic70d}7rAn15X~+yMY6=iW zN8tR##Kd5VKhpZ4WNN6}w{Ium;o+DQI4dqLnVNv8b^z7La zQ+&|4abt4r+BMAm#jXy@=FOW)NJt3g_@H3Hf|wGh;pfkvm@=PPvu2Tt7cXKiBQQN> zm-g)0Lw5-Bz{-^?nROIWCL=@o*kZ+s70mjjNYRk5W5}C$CN+~moHz&lsSNr+UvN_Y-+R8jvYI&(Ke5=Wy><_ zCS<>U{rZJZ(|Bggn8B=5CSn%arO3!g{7Q33N=jnZOUOPwfBqbwr18|OS(90(%#`U` zzr9SGHjT`lJsWez15sW&bm)LNZGbH~rAn2eF|NR|7`i(!P3P|JPC9q)oErNI{;XcT zI!!x*{QrwQeE5*$%9V@c&YhbaKYpCtxpRkxf`m<%5QiyWzI^1%moFqXHWvStHo&Gn zxGyuPSFawqapMN&>?ecuDwU6qPx8;iB!&(h%B)vbs!*YVaqU z$Dcoc=!r3_R;`lOu3alNYt~GvQ>PB|zOvHb!GrOt#v%`Z9qz8FZc)<$OJ8QK9m8sd zt${(8E?wwGwqe7D5&5-4N|r2%pEw858XrA+l)QcWmdHQ4OlL)4LLFQ=jRxjN;kzA0 zQ$qzH0=2)tKjw@Df_;IL?cKX~$-sdFNnBhUsaUZheyR_NiHWJNZ+n35f>GOOz)u4r zgF|-$UbWyB3E@PIX2XUJWWay{m~#xx4HfY8^yFd+ODn+mZ{NO6SLmQs2FG%(R&;vz z?oIooa`%NaY0`ugE?gKtaSmYo0OJTPNbLt`txK0KVeiCRK!0%V+`04!BG!HBDS7ZF zwQJW-+P!->oz<`^a_h~!KNVsYY9_grz^e0)+-;HCw{Oq953?k(6yu6t8)KJVq-L-uAa=b<3Y2CUtvrbkNoIh$N@&J=rFCD}z zG(>PhvC)heF@jkaD+`XkF@wa!L}q<-60=ZOfir@cKtB)8sb|&AN^04%CEk&_z{(!0 zej1BesEZ~~o{YD~2lwyams+)I#jGpOnlfby-T^-taN;+!P*eH&`EftJ0y_rs=FQ8j zKM$&3zdk*&kav3Z>cy&YU?T zxw*M9?@5K2g{ozihmMxKeEE{Lw6$y3X5OQga&>j3izCygPp1dj#>U2CEv(C+L4%n0 z>WFk%>jw`WXY)=1o<4m_BgmXSeVY1-LLS13-@JK~eqO6qEz+n_BkHK~FW^}QzN>I# zJUFH#B_+}LS#WZm5kRos3Kc3~&N!l?qDZ%H-7x1kni?uNAV4fjGf3;91J2H{X$`!T zz;K2SA5Ps5jWP-|fgcZqYTUVVC-ow`bm?81LH5<8)Oj|+-4GE6XuqEZz zty}o1V>EBxoSZs!3UkIXfBt+japFYG*^eqity8CtLwjGe1YYmpE2=jTgDa(P37+a; z&}sXk;GYUzkIY_i&=movWiU3_AC&_9AmHz>TD6Myd*L&1#MT=)tkC8;EBVR!>Jj9uWmi5Dj513Tx<^;|NtWxntU|=9sT5KAst$6AJ=YZwu#RLDgs+k(< zym|BJ@q+?^S)`rWVHzqpm`s>30doRphUXCT;NJ`WBmmP;pFe+&DS@-IWXTdt@kwxS zF#R)wp-!1Hh0D|21p2`HG#_|*0T~Q6G&B@b0s$XDIQn#k3PE_Sg1v!r1-9XJ)Ss4W z)vEn9R9GH0D^j#0h_!xgU>%Ul8&JnoLxs&?RzjTIrwtJ0ns@s3>z8b(uma`h=SK?# z*$XSY`e6q#HB>Xti4q9xfg0+VF=J>(ko|xSg;v02>eQ*26l6yhEn0*retGib32_Ms z2*9i$J7R@Xx(^>dOme_(K+IbTUOdyS4h*Du+G4~4R!Ut~tXP3rL3V`qQ9m3B`u_cU znwI?i{iUy8ztX+dRsrfQq+W3N8n3iz(sGq`7$V=TS+fR;xWWrUYu?>D3Q0T)|ntW5H`4%Se|Z3>_F8H9tNeb^AvVDLo~%lj~-!ZdNMWC6d*vs z=+UF8aa68cna;b(-3Rs?EDOawXU8j+!c373DMT-{Fu3fvNgoK3T zp09zx=^(5+2`QJ{0O3$RtQqrr9T2ln6O}JtUK&1pxODU8 zO{}^$3>`X@`F%pl1;++y$(Ai!^g0xoS*VL(avA!yaI~Wu;6!EC^@Wtn*Vh-Hs$;~( z#Yv%|p>)i`y1s^D7V0iIp#)aM!o$PymhGo|_wLMV3Mtq4@#FES+D}we6z$%@$%3qF z>Lg~N;esOx`}glJg@lCA`0loVBOqDV5K^vjh0{j{4H|@hL!cJf0LkLTi%CR81aX{h#-E-Vg8be)@gLK^(68UTa*RmCS~&YXSq>viek>0jJuO$SbYoOODl^59Hi7hv)9&3P+%P5CotN|2O?rWa1% zQ`oB-IGs5|-Sq2qHb#J&^~@QQOf^OMr!bS%b$X20~wUeW|A-Ujm-r&H-A5BV+>6`sNQGeqNo|50#A`*$@ zrW>*~dqgZE_2O&{IriWlx>{yoXlO{T)?-hkN=^kn#JhHV);I(GCYou~*5Q!BJni&X zw=iPiW5PWwHQS_}Y&&H1R&XF2JqASC-EE-S10RE1%w4XaVT_%i?rI z&!0=<+~R&4`pksQ8SZBN$ON8-M$Wp>{NYls%)+Uo*2 zLXJFq)`#dtRrKf8>>TKS8M2fXlFkdHAM+Dj6Z~y@30hVRg;xK|O|MwDislXdP@n%~ z8BN%ozn9rLM7st3`Cl#F#C?UBKg`+reTN$Me^@+Lkk;MA{^>si^KYML$Flw~SvFRm z+?Zf|Jz5{gyJikQwKO;#_Q?6S&yRm^!2;Dmd$qbB5qoad^Y;IGxbwez_)q-&|C6Od zG7Ch(U%c=Oyt@V_f9Cz4`P%;*_wJ3y#GieCK%TvQ`A>Ws0{+0a)NlCir2V&4_IzdY zH~3GXB)8niF#e0A{$wUErXu!hjK9$RCqw-kJl5l$^7Zv}7VFmNQ9X~bl$bh|Y z7x=fC8`ZF*55>P0{6%#C7Ab+hqW1@%8UGIVeh=>Na9113rv}MA_$R6V$s&JU@Hca& zORNa})BisR`&;wlUtItBU+jaf7UL}fKO$g=e}#lm6oW zi0IU-x{x6nioaC;Pwo6i41ed=Ur1p1U2^;zWuESL!Y;?(yR8hD{|n>4;qd<@x&2#6 z{$G*X|H$5dmM(t<>`!z5CvpCl8stA9`CqL5FLFy|UK>9=8cwXQRl6CdT-sB=)l^3Ct(_5?^kIZ@05|HxXN61+xiW_5@V9rK@+ zU762Ugk3J(TygJ_*Ydb&n8GHl;|s$-qh}A4T9}eB)z812|B_9hwcfpE=@t=E9;sX? zRx<0D9>E2R(R^-Mr`iV2OMzi)WhZw_{F0tMfIMPAvV2t%;TcqGd>oo1m*m3cx`Fsp z7fx8$EU*p3Vv`qgZhHq_nt#}V9kzQKexS=&lsWs7&qMcm!ujK}R7j%Pl+a~+Gx{nU z>w8K3Q5eBcn}l_wOdBU;_R5EvCp;SXOxs`vqnw$da;Sy)44KlF@KN2i#I6yh0NtFi z0Wu$-C?nwZPg%AFI@uO6EV}Q16|*p5aeLO$-8CtSJ*8V+dT|oj6=!^$uCg*9C_V#H ztG;q?QBd0grQ~D#;Kr+a1-y9>978cpB;kj z^`z>ijAr}Gw>3I61;LI+i_&1Ip(DyH zQgfH0m^5icE*$CHdHIIJXzR|SJt8-X?BXklkF3E{^QTgG%8nCqo7d2)v2=|I!^p5{ zv3dK^GOK(r^$#s*(#IXw)$?Z&=mi~>nf$OlaZG(o?T&9m?(G8A;gDq$poNFf^|6H> zPG7hVu(%X?#Z%g4ghBc!L(@TJp7DsZv^bi-ihHQ}^C zIRqf1F791SmNil1w><{&OMW)z3UPW0()--gp?{s{6=7!5)T$2^r17nSL2^q|;KsOn zR~|Hu&%1n$JEvOA1Qq8+336Lb=ALBm>UO9;eP)A+ z{X%KoTbB+Gbw8TP+w?Q#(IkS1o6e`Of(CUD41+hfF5PXQLgGI0dM?ul{Ea zx0K!1yem9~TXq7)QYHw3V7#-IHY87dSIU9{Wf7=$#}Vfmpc5ebt=tN7e#K(^dGnm7 zt|2ep^=i&f&~!As4v%8K3X@30UtDhYTH)(a@k85xJ44^>{eb@>TMBPam6@RcJ(Sc2By3YmJ?7Ht=|bL@9!xQD2tB{6)!1CyXV;BW zuX&|$owY2?=SL_tDLN$v?S1G=b^O%1|4m}~wjLDVmQ;k-97*cmP_!|;yk)J|X&^Ih zb7Q3Bg7+RQ`c(Z+oPkzdtVw?qS-V?kKJBKRu9;`{iopg9#K0&H+{WWMhDFVvBFm#h zlRJ1d+9IRvm7QpY4hg7Blq=iAJKf&q-fa2pyIz~DKx#cR{8^5;y8_z+ylBwkotPdZ?hT!jHdY^NUJ=zmFl{&d!HMQ`B*oSkbI&r$GP~Ilj z2FAAfj+7Ggmt0p6fBP3vF`g}?(>M8VQfF&|snkAbh&vc&+$C-C^H^TzxdZsxEO!|4 zNFKcAG<^oTUw*`WE;#FrchxN)sj`Zs{UODX^t+>5aUSO<_m1A4#K!ra7rgX#P0dh$ z8&(;8*box>oT-v3iXcnhOfhfp^l;gyY$6)AKRujsF)jy+BV>xrQ9OCQ zOr7|vxIs2eGda~d(B?GK?Udcnd$}E?v(f4QfiDo#coSJRT%%w6y5;h49>i`#?Y1N} zlNBb=4ci$AD>>PZWM!80w#k|3if)OuLDUVQCJ0ORGN8vJVrNA?CfufPU<#M?&|oj> zE{XdoGn^$6sP`^QIf;sFP6|9^QsoKIH+QKkq?(wzOE602sw_rsR}p-TCG*^xxF{Tn zewR^obel_d8t&pxEZnd{SF@7pw4t?VZ+@i2D2R5}CA?VVtM)BqPu>HDjy;8U;EJF9 z4-*}?W;_PBG?_4&-ee*UaFsQ8`eJ`kyoayzd*cATK8)5|?4;6X)i9TuNQbgaO?&`e z>v_@ula1lPu3i@IMyHaG$TIEIG}vvxjWykS?zr-j`X&B^eb}dNbM*`TDxWaw7F98? z`y!eryk+fXeY~Yky9y`I7tViYdF_vJgQeDaHLj|L@&siZ?BAoF3CfeklP8H1{Pa4! z_^Fk9^*5aKCD1hDN*lp_moJmK);;lidOQs0*=qPm-b`gJ0PWxB}PAWL7J9ni}nro-P$T z=^t`p!p+rLwr#3axoC=;HFu$p>SjPrZ+d(6@4Mj@j+@)vzbXiKNv}?GA1{U+RQovQ zI;;-2Ih6$h(F^cdLFqio@|3|hOERcni2Zg$h+RIziRFr<-l5^!K+|p=Eng@LY@A^p zooA?L3!?U4Y3m}~vG6jZfM00)DjuV7GuGU|O4RE@PB0jCSHKt_RJfB+VFH+?qkfY2 zVkxb|g)-b>hi@pvcgkAjerV|N#5xbn46Nqr*K@I<_WhsUAI3qyFbQ3D!>fFP>|?kQ zI07cNa3{7G?`~=Ip=4gmvo?x-x}Du|*Z#9gX@4GN0vsLULsy8$R_>-#)P?Vl2g{0) zUNmwn6zq9?cFQNWFc`4Du7<0(n-=GMe(B{8?}2RI}a3 z<&@t{(i$iDR-aMzNC=6;@Ecu8)t`|UG^=!I=KuiR%Y|a)PWG6hcg8oqksl$+1NG^E zMeCeyH^_H1G7h|aT5)tg?OPp*{h?qQdp5)jLse(+kBj^EQF}@)CY4ur^JqO@ z@>!)`Y{|wF?>=+Z^IrO(CP@9c)Uk{Ih0$z@@<`sx6$LN4^ZHSTwifyEH)Om$tf_?^ zTSCE?B#t`yN0^U71xoE3&Xsr62pw~^Ry{jZ*tiwgjCr#EBY&rY1I^Q6ilMWfiyC~g zt43UpBrO@#cbyA{=a(`V#yZYNBqqH5(O7k7zQ7I=kbXpPB*6j|Yz+G|!N7;JvDSNP z6DmMvRnBZcqMZKp!r{acOK3tMS1$2|L+G;)?Ztv0~HB!f7d6=MXwtMm$GF@^z;fFk4~<5h=SI6$J@- zu%`iS=1I7haL{U0NY`jhv7zGle7WlbTzAJ`H#dK>!=69?xFfE8lhn*t^wMx}?EX>3 zYFIu!)V(Co(}*|m0$Hhp8A=TpDD`yn#&ZdaYt@41s>nRm#?8hTt;-Hnya7Edp7;+k z`v9Os$`ny4)~ zM6CcrpH%YivjrBOvz3?F_PiMjJ|Mh7hV!}}JfU8hu`vhDP?WlX#q{9MTRY>Q#3e~M zUO)!fHpvP+J#mi|vwg}^PZOqOnL@@y?t+8YlbFpxD7PP-&w|=Pg&IEM_3GcX`U?1&aA+=&HeXIkM=;cUQcQe zZLZIrlMzW{a-9J|-mtv@8jfO;p3A8w=*3aZ1iaz56%>6UGc}#2sa?9HXfA#{& z%()CP@lRTwiKa9*zTY|wuMb>paXMKTGDU7Y1tNen&8=$H!pywKL8lF(6Fx9EA02@% zzO5Z>*^JF(l((L9A=&l@DqJV$kZk)gGwx}~d;Gm)cCsMqdr+lIq$y2c0LO-BMuB!c zh!j>3=!Gq?0_N>m3psAwmG=O{FR-}qx=Ke32W9b--dK!Tw1*Y8JhX_&@+|bbSw_|N z*=?!B>FEtEydux-4^b7#Y&wt`x^&9-CF+JBic_^r)Y_OSm#bdnj;}s)wpr#)Tk;Y$ zY1*Gh5Y~0HO-_X8o#0x_3w8e5U3G6yYncN$K(&gNTRcyAlL4H24{qQh+_s9Qk4DnH zs7eabci?EW#;HA`mqCy(3knf&hFy1BYR6kRIOIxx*xP{N%hfhbJU=^us|o08X{M{2 zEwNihVFjR5h*g_(Kgt>SvzH(7s=c zBgP@h=Ag||eJenE^|TAYpGus@g=<9ZZ>Dz3|LWq%$y5?w69uNMT4m!uwt|#?9yFTV zwQCoUC+lrr;Sp`~(W3VH9BBi=P3A#u3es1PUN6?pH)k&KbcQT>4v64Wj~?@`@YAcr z`uhZJ%6&VB-LX`AuJSnv6*QmvF42^aYP0d&)u~F=woiM0y;mbD@6^Tx3Uu+pT1kkJ zxAC5kB}dr{D*~IIcBJrvz~H4zA-dt!6Jka!{hYer*GzC9hx$}AaqXIiZrUS}a=WIX z2i$}$4OeT*mSJ;Lleac5($C6;A(tS^t7+pT^zDxJ!beUwY=;M< zh-0-rWuCX1Nb8sSRU|j;({tW?Mee+QKHqHgGPoOgT1J!+4+C%04YCtD73Bbw)TZ23 zJVK61Tha|Pe#IRi74%w*H1=*jeKf(SG__tD^?f$`XVE=4PQRFm@2Mf$9jHN<|1yG z*)z_N<74zE0d=6n$UEaOd9M_$1mBT)l)X{OS>&|9uO; zR?s?e&OvB4eWx#7E{NWFxHu_c1f+8e)_J&pi3kU;zHY;rnZ&J{sj z#W$d9kgQH%Q@_gKw9l2qihuG`+GY^NAshUw zke26W1{494I*yopJAYAWj#C@fuAX3=1Xm0$@qR62k(tZD<~`Q^<5u2U%|sQnuVx@m z4r8>{@auuZTPUetO+z|nZVe6fTJJhbFkPk}Wb}Mmd}qc^1!Q!tVLU#CDU~6K4Z3bH zei@CCi^TA&(+Q4e_H6ZEj(agV2?RwiGw(6JvzWr&bci=li;62uzWwd+=6ri+PQ)U;! zo!k(;F`bi-yj%1?lG7OXU0skW{v8Gx3=+VIy*MuHTqhQ&lEZ-qZ zT|%FnJzhi;ZZC>%#Q*phjLz+RH2z@NP#7*T#Q*tgbfgz_IaqY9zj08W` z61E~UJp&C_?Wb8a*MHD+6)@bw?Ba)tygiVpG=}8hl&@d5%y3a|Hn3S`aSdsRo^{Gv z#IUt$tdb?*EdH9;nePsaxh4sR{D@`zsO-qkwL6r0O8T}Xc3R5I{0V_%H`EWDzhMbK z6V(OrD>uEfJV^__So?>Qn&yx=C}LNjJKnJwl@>AaM2X*agmuGeZSa=-?3K5esKYYM zY@XCKw;^||^I^}kdnp_Dip>$P{Z=OVjf++scC5Ees&dz;v;B!a~esrDH>a*<+ zfli$$txi2y*q9=HmTt*r#q8>Y5u@i}=YMj1b03pgS?87;U3Wxx<-OWZVWteWMq`-~ zyOObDyyo8h@`lDOyGhrLJg(a}g#-p;Q0DGuj(M3n6XC|KMY*=TYQp=FzX^nTJ% zpH5%vbeP42z;8k=Qjm|H&lek4T(1z}H8(R1Y%6$1Iuwv!Bwp1Ya+#CV1BO?UST^rI zTMN4uGVN|7)P1!)+z(VpYsD??0W6C+M5&lvn&F7`|DtnFN&+JwI~~0ikZ@j$%k{t8yNqF3-&!+8qdwI zrI1aRW8R6pfIOX&@_5>@`3ldy9~}l12ZwO!91nc6Z_1IGQ=>U>F5i86TKKUi&kq?> z_~b{T9W7!qamP`PMIRd+2=8t5T{BlpUrw^b=72DOz%dV410KLtmNUzy>wRVcNRwiq<@dKAsoA-JbRJaSN{y_rTAfI;=qCz~`_0GeWzGqO(qpLxws2XM zV4Za@(RqqIy$OboeF;0hfPm#Jui>%|A*uRTsn?k&Ql#W%>rT5})-`JY&K?<9-?FMe)vHP>T31Ts;Z#PT& zI*1&QuZk~kbrCoKM^M?HOe|XOJd9er1kSMhBumkMGw0Vkz!E7{{NOfA=lYi`UFH3x zhO#1q9A1_0z@Mfk0Ek~F!{G5*ilN4=k!0bAL%SZQ5%_~gZYpU&3G zc%Ocjqj_`nfLdO?8v#xi*4Dn&<&PFvCSba^`|5DcGG9(sK|&4chGsNe$fFUbxAKyO zu<%c2?3?;}gV8lI+LiYqNo|%_jOGO9Q@8VBoc5W)fqvXH>RQ_3zIr}QAZatbQb+e= zosYR6DcU)k8x@CWye%YOvFtAd-?M)6{gcP8#>COx*UMeCTcGE|n+h*66cYg6GMRhQ z)>s~t1Ow7F9AvBZ@rB|(q6YiSZO5b}(CFqk;eLt-g!hdReHF8A-{1=%{lh2|gXn12 zUa}C*T!|}sjXM7Bg9s#2U*BiG={>qpBV5417f1!Hqq$A z6Dz&+iZfQ`%h<+h&+r0ATz4hr;=ztI_q^~{d|2p&;3G?fee{klC)ytw6l~5TO!6G9 zZvl!fY602cgV6^Dp`)JinnQ+%}w#m$1_041cs2#@jc8AC`Diw7zqZwAKXG zuywm7)2$5BuN7TvGt>tZ1>ez6$$e-N#a7UYAw|f}BE_iHO14UUjfS&xp)Oq4ge)$v z$F!6rsNlJAfyEz3Mf|}t4D3xy_BO3?H;~3(o_6d$Quan64^vkRv|i*C9B5501j0k# zwFKOfT+D~i9dg7JU`UF+F}KDwfeB__G0f8a7hdoY`}4#sa0K(V2;B4vsVcd#kle(KP z0vqXAI|99?Up-Cp)_ka$4^o_QKy&U5z8fctdb~~wNRp(x2kvOyKznSz?%?s^k%%xk zWK3|+RnSW%9!+}`XpHc8H@!;I?E|d=HNPT#yzx^#`_$YR5A@kOpdA)prHSzjCcXn z?*2|7Vk#HtePc=dO!IUWRB)s!Fz{c*Uei9-C?f=l*qmIp3SMW*Q#f^--R4CSwO} zn8&O~>%V*vp$1^{?T{?+Zl|?MKM1;o6$^{gvv9Y2Q(x~jzopC>m5tCV7D-Df|t~C|v zRm&PWrBa!1+4<4 zXLu_$(}~}M3{^5$%r;U&^>r41!BiE z9|1v`()iFl83x@Oa=7ez9TfozSE$dq1z*x;jh)xssJ3r?wYjy2%aMV#PVj2fU0R`H z5BFSlNr?14(7@AI0YPi#IZ*Lr@o3i*nY;Ue3d2Ebq{zTLF~DM_$pz%NBgz|RSU4Mc z)*4%KA?z7N%NfE#q4B)rOLFb{X$6+m8kO)9@4v?P*vSB7l5~RU$B%=lW4V!Rna8`? zib-F5Ap{PK)L8U6>$&UWw8|V{@qir_L4q7qdKI#k$xwhXE#@@Uhda>AMI1qPU60&l6N&wA{O!{wJLvZ$Y6A+0ZbHf2k1 ze9vlU9h!%jQZ^IM7`}}ds37r zvtF8mn!07myXu@|Pdh9`VNu`Vh&L}c6$;wSxH^hu9w!*`A;2O&LsHKL*@7*qUoU&;`$+MVht4 zFIlasNk@gW!J|v0k}2MMU_02@w4dp zR2nBUoG$3y_&~(n60wl()-No)T3Xn58HYnD$)U|HpwwNw?)W(Tv&;^r@Wqo_I)lW; z#?IJft9x38IW&jE+1OD_yiT#qXR~}+U!9YMH>5_%Vyuo37_VLqo;rUuMRc&fDc8oiLEYe0v?h8_XQJCBD$aK^lVK{8`};73jGR^N6uh|_ zoD_O|=ra~wq#y$dx1O4r1YP7q>S!jT+)APIYH8FVPz%@H!Dr-ZL3+Ket!g7aGiHZ!5Ek8x{DCj%f_5k)w+pZBF%x+=)H6RL_{TM-3X6aP>2HgM>aNg>uPV_l+f! z5bW*ohBJ0I@=%LVJ=>jnroa8ISK?@t*?LBnO!Y;-;s|}AAqRnJ;}{3Md^OLQ@FV-E zG2;#)!_^XD;K3Om*{o9E^+-pJ4j1)oarBVek<;su!0dimQq?P&>N7bBje{+d@rk9= z1#<2>b1_7F8xG~eHP?J6XUPG7cIvN+LnzGPaLOihgqA?)=OKYs?NI3%{UiFaCBgJ~ z&xNTelIc0blo`j~ySP0cZ5Ut3^5c(QH$~3@ZO`(J$B5g?jfh#CUtUU|Dypl}cw#mn zZ@6jqa_N!1w~A$o!4D=-aPIgExZ<4qxgA>&p4tSucKt0bZ`mi^_}L?%;X|j#e)F{( zlZ&mMDLs0H4<7EZ`ItaS6+BPI7(KzYL2CUN5<_dmK;rhL_`i8eha zIR4;%K;zSu=eyjOW~pOACe_OxqKr6<>k4h4Hp0#YNTN0%)BL_CXtg)lCRM6m)BfQ{ zM$uA9`)BFZdwe3Zn^bhf3Mog0Taqx0bz*eXVD*O#!X3FyW-EMGFW#Z!Ap#rFU73Pp zTeByM9!>JWR_1YgwVZ~5eX@mL&LV2t80N#VAIt-%7ec5k*0+|JrE$z2!SF4nbpv>f zK_B_Xp^Knq|BCcMSoS-OE8JQ$=Z)y#;{a81hj!SqEgN?-_imxr?ag<1*o=m%HrTiS zbI4t|lQ`8s^S(9EE2<@@MYfsaxzTinEHz6C5AxpDsX$OJiJIjD>chl)a=Z4=2E+OQp(* zxi8KY&xb;fPx8^^H+J(*Pb%sHPQgm!yTfS^WNo&)$|C^O{4(lACoEJ&TMK=^7SwXy z+0@x{Myx7pM;F~lAU^e4k&j{$B9VYCjhmeM;R8{9xbKqe+v(Z}=PtlFJln)1VaB9@ z!sLk0RgPOGirtDCvGC5%X?>?QdEUb2p_`|GKj?;>>L7yD+7!hhPqGQiy5NSs{kW@@sTxLyff_H0wI*h&8=6^l_7p*Cu{ z%9&~6K%c?>M#$SEs)mUa@X5MVjKjisjYXSHhZIA8@tM^U{7827TDQvm z#o;>TKv{>up4a24=h%F);pT=Om!$UDedFL0JW(6A9ZfehUk*gk9=q?}8 zWwsdSsS(Hmh2j)3^40);fvhr_p)#q~NwrvJ>O0CQAW}(IrY(p5`jXZyr?|j#bGdwc z)9(yxcdU^fg4YEyS_z})ecOb@m_x9e!v7gLP&Q}^uI4`F-*cuzyiwhCf0z-3r+RN zhsp9WfIus(q zeeV#bwNIHhb_YXOUl9gUtKFfL5HK0x!SVe8zTb z7#q@;v2zg=i#~evR-vXHPvTz>RlrAG0cMHCk~i=pDH7#V9VACG#9TD0$G6mlRa}-Q zjA_m7=R7%9OA?<=ZZDry0g*l<(=_y*Oc=*x8h~4D%L2?ur{$cPvE1srBJ?sF0q9mK zB!z?-MsD5~ACnuoi+S(lfA|yMxLL`-9&mc%q5ykd|7svom!D2P>aA|*c=zi_*uAKj zAME1{VsP{ti0=Ks2lnfE6S{pR`@_z9uf4R=PHS9|FoK>M0g?h_vfXm$(+-^IAyDav!6)1_QK|9Y%G4C9 zMl@?m4-RkdrBDW#5vanB?0aV`{d3CNMM%V&WCg=9BmmEotWulCJ|BU7NU zlb`03rp$_x+-6K&eQIj=^?C0mvmQ-|HES&MESsDgYY)=i8!z2)yIy(}>$6ARtLf}*I zD%O}O&R-e#Lq(-bxT;GaTD&5jIkus%vL@v9rRrNJA?&2v^7nj_sjWDIWd$^ynNouq z)9&*rlOrmk0K@u67CM(k0SUINE#id1gU$?fBIO- zXtt=lC-FZHiepeK&o7MSrOuMv0r}M9@ zf^4{-Wlt-2C^*IxgXa~nwyAe9Ll{7EOq71o^d*%eefUT|I_WreGX4iO5o8s!m^2zl zUmZRxB`8bg(o;w2)t){{5>Z{I<0h*wlPL9&ixC57IY0!>pVudg6m`VgL<+E3sfK^O z<{DjE&|6QPXJi9gb6C}6Mx?yYwBUL3Xh}MTkF*oL7iFxsZZIIE@A*k~czp-PwORW` z5wpZzct8b(EPT7-b$SR{pu*N_eId{N>sqnZH(J^*&2Nrc`Id1^h7^NQqigrMJuCo{ zjF|XjGSV(yYU8`gX|u)0O#tUF>Pn4}Qhr#L@L(CjS~XyRKXeeU$h*5a>n$%iKQtjUU{ zOoKmIA#UU`XkH|pm+`sOXqr_nz{X7zHP|I%VQ(9_)M(#dX@FXLx# zBTzQ59PL}g{ucc?Qc+u2hC_Rh=HA!iR$Fl+d`Xgc?n83oOpkiPfhwXvAyoE?Brld* zTT%psvXSH?z0d*9(;MS&Ijv_4*4sW7QK67ryc6f?{1ziGg4m6&9y7=1vk7pF8otO` zL*J9yp!8Eooj=b`LsfSwH=786+WUC;j!!u^tpI<#1}|uZ zVm_xTlF#)<7=&nu$|DxUY0o>9$4EwDJ7=ti>bIBfzFc*bt$tTWOdRmVb%7zzB%`&KE;I3 z(F!FmVf}U14bj>o0|Ki9sb<}8D0b#lkF6_g&B%jCR!J%WEJaPxsEn`IM0RoSffiLr z5vkTsOa5sOMeT0nsm$a`PrgcB&A2_h9_|j{O+3?Wt-G*g68B|)=k4i{`Q9seRfL3l;V`MNx(sRWR*$M zxo@IDK@}$LGgpcuG|xbS%b%laT_Vi2NqpI(fV&n`7P{1?dObWfRJCu$aI&Cw9kivV zX^D*gWXoCpd|9oIBPGEE{7p&|;hfZn`Y2lofZd20BJ^y@L9!!KV77@oP$VX_a2f3U zA(LC`D<7xU^6+#lINH(6Hfr!4QQ}NK*U_Ca+oTJqC`+?a5y<}3ONXr$ z&HY<c?^bXI&a=4-$xd35d%|GnBQj0A>dNAV^q?b#*3Ieido0_3rwAeRi z*ljJyA&arfN=Cf{x%XR1O0`npnr~Q0rI2?k+buc`i+{K6ki1_#c08+~o8mw!(3mu6 zt!x5rKXF-NHbjr4P!clh=ab@)-R4{4Hw1i6LWzYAZRUN&PaKBNb4T#>X`-}lb-nU+ zYIH~m%8F-TfcM=UtdEzl2qf?ofo##U325jt;5eJd+mMArMZuAn($vaB7+B11mXm(G zoR>7PDRs3H*B*S`@bX+s6{Ov-^qm59rdHQsO!_`25kbhhtw5ValtaD}3q;@FbN%RIyLs_?FH z_@VF2+$3-3i&kHkg!gLIY!E$YZj|hV%@*&&1Y#>s&E8j)kC{9*v*=5YRXti;+A0Y; zd*=Z(o*S=+EjG>U%%qv;?<5-6!lbQi7(-s2yvbX`@*v z9{_GF<{H+E;W^ven*zc0A&|JvcHGy?u7NohAJ~sryz>KHJyA|B)o~Evz=d>Sjf%|t zr_Ju#Iv`>|T~>}-+RCt0f(Kj@0Xyen-+?@m_JnG>_IDc_|ba!o$wfe)S_yx_4#L|_T6afIBZ8$2e{83 zK!Wjpg5jO*-;XkOgpNLFGZIJ9(sZYcl3jXeKDyXHFLFWJBQo~KPoIGX`PQMts^Z%; z>)eQ^JI$k-*8}UP+$QX3_C0?!l8Vsy^J9DUGYYY4Fxi8^`)!d7_A;>>*!Gz=*tQDG zp=bLyIwZQ!P&;WLIwDnWE4)og{0>iO7x5642yje_Sq|eUcWOw`G_=nzVP&gu!gg*m zp82-*$a+(^c3S{y3|jcW&SH&kpmw2aDv2_Y873a_45IKIa(nh{J)0E(h{7RA+`-kT z=N=Q#)y1eYqieECORboay=M61gwVFqJhQ-12H2Lq+bY~SqUuhD!iZ;@)}|R#?}*!p zZkNAL?EW(YQ7M$^<38Xyj(vRX9iBCN?2pA56PE}>eL)oi}h8b8HW3y;S&I>Cw5X`4bA7m zT`hS^5V~-h?Mn~w6pJ(%v#RCDP4K4pBW^^YU->3KG51Tk@GNyJ-60iqYOju^lw+hJ zQynRz^Qw-oPKQ5FI1Rp}i1e)>hg3PvL%10NE@{9rq#nTDNcb>*?g#hTp)s2sY8tF+ zW_Q5E!jlOKtd)`$_Ynf$64bTV+}hVt@jgnwMljt>-+n@;vz>jS7k1ix5PCv;+*dBIL8$pssBdf+Qt@efL(FYD04Z6UI5kW zQqwni@j$QAwTO7)zan*xEUO8j3w=E*~+OduKQk#N#GhFZEq^N<2 zlan?T1Yz!NCr)qT%&h3WK2NQT*;%r(_mL*0SLFAZ-#8uD*j%jHT7KWX(4y@hYs;~s^ut|wQejY9e}D1(Kru|8!r>(k=2!53>H8dzD$k#1yy zkFSr;*!R5{C}ILt1wFCJ^yC%zut5p$v)*K_E|kuXp6&CwL0SnmJuRga8Jx;8UwwP& zOOzcaoK{ZrZ0QjgEHPyKY*&}W?l9LL;t&DXfO4Y(Z~_=LJbvZfP0I&&+lG1}Q*fGI zfvLk*^YPcxyT_Dj2g=fecLnyR1id#2bx%L0hU0tFh2~8W51q8hPxxxIT1hLd6TLLE zV_wemjU3238~O6a_LFJLLMPd_$3F|UNcH8obt*4vwr#f+9O1mQRoI*7(W&ncfL^(y z+}_Bn!-;)gMS!6fd6}nMO>Ux_D4h4c&8@Pe(c?$W;O)j~IdF^SBJSfeAIizvMtehM zX}sp%hKe-UD_SSetkMZ=10_c9?RKQT?==nr9m|JQeF+;V#RrX*pCzZ3o4+Ztull?& zE+l9*>-JhCy~h>n8ee2!8FYPPa}~3j|7*Z1Zezyz-h#%Z?6>+r!1;U9{BKl}#(z%mEOsw?qFUJ6C1hQzJ>M`Vs^woKes+$EW#Gy)Qf{ z?3si)W0zyD@wdwWc6PU{GH8kKxCo-Q?1=R7Ix|S<@LL7#p?0f zdaXmC-NmoOoXW-D3LKo^TZ`(%=kZ0~b&C5b7oH#lhF&p_&L{qG6f}K2;h3y`cqmTL z+zIP!N0x~=burn;p7oK9P0F!#=u(Dl@!_WI#8cQh2uyt2vqOnK536M^TgQ5lljCBL zdg(2FKq|N%5FMK*wk#K;T`j_O-!N;?){LI*a`95|&G*$VvWEDqNO?{m<712>(eB-1 zgSzrKs$FEWGdM_GF?%%09H?Yz8WDsa{-_DUr(JL_+})l8ZR@lE`!)FhL^ z+rwb7pfoGck=ZT!;y_+O6Wk96MO*f0#p^fa?O;{T%oCq=vfG31*dmRiNWD10$d5Yg z3E!oiPnxg1-S$gyJiNvkWVNSQZS!8MbW$p3U~eE$-NqWvFIrVw=T$MzSOLCXZq4Q~ zRxeXdJN$6ilkNt-iQ;!HvVk4C$T3rU{NPtO+eTL~50fSMfd|;2ow37n3ct|^_eH@R zjW%PAoKE;y$hPvihqGkjWp7zrrE`{qYx{DZu2XIMraX@kgX&B^W)n3LJ7r36-US@Gu6bxwNANNeIfe@7==%D!vD<);2j^m1oP+<92L_>%iLz=rw;U7FCgI$0ST0! z`@vWs)eB>=<3RhDt2BLO+h@xNez(8_f8}Jow`@7c_$9A#QF&J1$hS;#1-IK}eoCRZ z$W$j62zA7(UH1)sc<5^R?PH6?TTROIE{U?^T+{0_S0=X798&JEgQURrmAB=vdB_xF z=YO3agYy0}uY1scJ2ael2%W_K;e;k24*OLe#e2)%o|t`Ud82GWWO7-il}#pTc%Dp8 z|EE_bh03J=l}W8hn!RdGSWhDA!O3JzAsj>lfL-+C*}YK*<_ z`@Q9{M^mN_9LVvo+T}@}Z+D+zSG@zs2?$TGyzpIcXONpZ{oR~w z$ca3m>$mCwE6nUIk3e{ReO4E7XyQdxpaV-%{POI+^EK$~8B?z6m434?E#b@ach#Z( z=_>SvO=XNet6YQPeO~x#+pEglXC|8xhc06y-WRnCT_)l;b(($Wy`XXgklUW2w94H! zak6)=ykt+`B>PVNjGbhDV60@Md5CSS>G;CoNKAjE`%UE>rZ{AkO&$53b7W6y^Rrgq z)bBx|U25-suUd_iy!cg^DNy{K;8fOWtXEEQ#rw%PlXLUdP)08!$E`{dPcoTr(W^_-OV`eMNDuLsh>=+t86Y_hrW4~5o4crVJmG+ z9mphfc{X`+fx7AKs+c^s)5cufs30r%kzzp$4;E@=wJ(e@k`+|g0U z8&%S4FE;r01AojMCLG^rU?q*s@TJE-=G2ERCgt*NM|rQX$+ekY7HMR&<21 zI~;#og?Di2Kf3#l zK?WH`+2J_u7$~?s83#Ll>E;-V&1x09(&ZT7AYW;t_|Xr~4>nMlkCe*Z?=A1&;e@B$ zVa}@~uYhH@X>3{s2U{GMbhP+I==Y$11#kBCm;iQ$Oa`6gPEYI=*Q{_?W!Q||Hn2%r!DXzZaS+JHM(SmX5n{(W@R5xT_BOrJ(+UlaSIA6ybXC6JtIld{ zoCl`8+Cvvpq!Ti2Z!u-Q2uN`79NSFF%{%E)0H{ap*zDS04*R2a6drFmWf}u{g4^pd z>WpSq{YnaMI~-fw_TS>PH+7|*XpC%h&`%y63@cA(89VDR-siEyZpPd;xG<1+V{)AL zTu5C6>WVCNaMZCIxsR(F>GOrXAoZ0CrOy-d!}0MQj`x-)zp=k*wC5_pTS&AKuENP) zIKlflu=#Tul=t89^iE1waD~PD^ybU^YdpU-<=-Ne?F55ItjXzE`Fy{4%N4OgvO@?5 zyR2K7*u-A(A`^$bOjoiTO+J4Q7uoO~JI_RmP3qvg%TBKi(vv^xn7n`4H+)uO)XiVY z(4%zy-({a))%keB5ubK`l|BF3j`~#+-^x<&%_+N{ zDckl^o^){3`z{U2`|o&l(0?;8?^gGO9x!LQARZ!<5NI+CZKc1ym*p&P}~>mT(TiU!5Q2{dYW$R`IvNu0^j5?kRXO zOt6eIpOj+HgpnWRr!XsCr=uohmE?MkgNcO0iA~AK9rYqVwTqP#@!Dne_-68@P5e1O z?ZRoX>a!iv#HUriS8#CUK;T)=WjK^m?DnZmaHibp!%0yGCTTZi=xrHt3MYREXdiuN zzZkg!j?VkXg1GxE#oXBsj>@p%c970ExL@$F>3XmaU$MA89<&iTG5Pa2DBjhn^TQ*3 z9D^h1b+|h=Zih!eb&;q1;EUyC^0e|+M$jv5+5EIE#|4=n(@#FHww~0L9!JmoH}#I} zXd8%L@#~;>#<~{!Kn3T$kaJmie}$*?rZ{hvIZof1P%a?oMCXLhgf%r}CDxpyNjqk8 z*onLqC%q|bDy0|8sg9HcI3`w^OvThe?Mis^;(&x{m-8#bIVp`EUnjGpb3d>P2YWPr zCOEcF{e|YY>)1erek;8uY(wc|%K}xG$1bbeb&8h5pSMeme%&}!NG za0#?y^~_bkw&Zq{-glOsWV^3?^vh#}4&}&3zWsGuL3EA_h})rnX&A#r&*#wE+u(jo z+7U0*f2vy{<0+%?U7kAMtkjX2b&|9w={ILpkVqtbNxKcl>B z{28x%(0?nB_!KO^%7cj=tz6+xb>7L&lSF~PI5@;pwz4A+CVk=r%VDDBoiAlfd=cd6?=EPBc$j)&63H+IJP?Yc>r91@$qEr4f9tVH= znHMJ}K84kJBb%hLi3m*{?6|*AgZJt3fC9LtnD(2;{f zc2@n$-`b5FEy~DDJt}^))o`R1=#0#Y_ppx2Tn8^Sf4G$80wdv{=&$)(IRXNP-eRc- ztaB$7?u-_HBkuN~-={ZU-e2Qgox)psW@Qq?PPk47=tVbr%gKaY?&=Mkyp`FpD7@vQ zY;DHjkxKe-vgC>NHrI}fG-dDgCzx}7Yrl?+ytfT-2ugda0iA3yupQw#U9Nx)@&`}p z3FaPpDNCDESDYu`R+#z}XU}-_;5wc_JE;$z{-~F7*GC(OXA8t!mC@E6BkN2^S}EI$ z#IMdLZJXqc#1!Qiq(2)ct=vg}ZQ71y&Ix;#C3m!40XuH5>!okQUdqDh6-=S@Zf$uR z`~2K$g&)n2_v!M2KjY;nop2`p2<5%Qxp*hxz(rd=G0}!UM{FFE@r+fJ*hyYYnF%um zOS>fmwJ#GaD(9j|V&jI$+%J*>PHJrgTQA9!Z5mecBQw_CTN@iul*D@ z9=$LW-nWHvqRmg+%sB0XQ3W1ohv z%i)A2*_CqiXj%T;R~;wox=i(wSDXj*MjxFgzK-WQMwjC&2kn*r*mZr>CA-A=Kn|;l zmG(u%WzcC)Y$3=)b6sHh)^+4J?NvF+lT6_@UIYR2r*cBt^H%0_EuFN)vMqm8@5Jy| zW^6pbam;YUaJV_XNn8XP$GLb)gWo96D&o5GgUgRL#tPylmt8x$&wW>*&vad2l+!ipcosuA<#IXh(lkQ{Dx~7Aoo?=q_T~>~>>DD{Zz@!oIRtdDJLQ(q zN2N$JRvEu3L#9{8fqM7vJboi#2>!@@R+OLdr{0VyvWaOEPq z1oW6H-A?DY`48I|2+3n-%CISq;$xwg)T_MfdK=tknV^ie?Fu}V6G)war4+~G`N@uzV)nRYxB(wkv2%? zb`s{8!ixo7v7vuOw2-ku>f2PCgbvA!sPze)tJF>VXfy4TjAtBYk1NnyXnsTiM>1~1$xkZoWO5?Udt?8VseEs`??d*5Cxjr2 zT~@YUHJ*5xh6>1hn@u>8?Lz^4k7cECG|#H*+qc{flzO>{Y(H34iR4x7oAym#-#paz zs8|2gi>~cSPxX9#`Kms{&bjluA(F_qW? zMK}yv2K=i0S33}k49Yzrcv!0Yhx3_-qsp+aIcPsizBj-(a>^tXP? zzRJE;WspfH+s8v$U5V1F_nYb^Wu9Vk+z>xUJTh*W|I9;W>A36X_(`Ua{)Ttl#9y&; zx3(NN_8*yS>_cOHlP$@(GqoR*>-J&pyAQ=T+oC<&=6L`BvgwCcA&jZco#O99NAqt1 zC+WK|D;xqhOMhartAt6+pz~m7uS6`%f%yLYbbc$g40FVOmBb1lw(ZzthkVvqsoRsq zlP5Abh;e{^Dz}1HKT5H>|4v2dpie-THd`iVpaAE1C!k;%jaOKi`JUa9**+^w^wT~r z3OEVmaSO3;dn#iA7kh~*zSSd;S1=D%U7T?2-gXd&X5Vh$ub`NlN2~+BT(kYRMCI)Q?GBPkKECu32t!0 zcR5pE*uG;k7q#1QbT**bbWk=aJ8Vg|Pj+#B>fu=Uym8y}Xsw(#f=Y3#Uv-Z!HKVEWzkZ4 z6WB$n|J*SDR-advBU?E)S)WC#r#G>G@=RJrxskkYu)liK=GYF*k=%7C#)%!akWr8M z>6Ps(xEAy`hM`M`LqjNG!hdSV;(Yq^QCRJr(!?s2|boFs`A00000NkvXXu0mjf2hUE* literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/backgroundWholeLight.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/backgroundWholeLight.png new file mode 100755 index 0000000000000000000000000000000000000000..375e0f0bce4e9ffd27cfda3c4e530f4c9ceea964 GIT binary patch literal 5819 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 U;s8?{AWFlm_fjl)W?s?%1~b_Y4*&oF literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/finish-btn-pressed.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/finish-btn-pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..ae99f273dedbb86d235ed37fa2baa64357ef7d75 GIT binary patch literal 3879 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 a-z%T_uhe{JJK{4S)LvtYEqnamqy9e~GABy_ literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/finish-btn.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/finish-btn.png new file mode 100755 index 0000000000000000000000000000000000000000..6259e4a3791feaff29dd11b4aabc1aef8c088195 GIT binary patch literal 3879 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 DzMO^@ literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--connected.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--connected.png new file mode 100755 index 0000000000000000000000000000000000000000..acb028d69c4a198d6ba4c5aed4e4d58b52c52b0f GIT binary patch literal 1131 zcmV-x1eE)UP)Px(B}qgXawj`johXyuL&`k>-^8Suc}eECLN->hzvsPAP)Gwx(LpDb z*%Krw&CT5!B)jqo;|EmRImJ9!R(wXWdx}}@!QTsJVOVOfr4~z!NZoO}dCP#48vU#` zy6^LyG6vD}6FnmrKz?Vff61oQlHHsYON^PzXQ%({t_w+2AqJJ~t>+J4hrPO{ZVmvS zd2blr2o_5WihR3JF5Xoih;J4Q*Tk$}TwGu8uOc8kke<#aqWp1qEHRDfQ{S%?{$(+k z=~@9$^cP*j>$=QYJ7Crp7Z&!_LXFz7#0uuGVa9E~MbSz;K^qgvj-@!XqLmE`yvdcPDtwyH<>I_JVe2Pp3dtY-RUiA^P~)6nRx zVrl-nzP!~Z!JS^~cijK?Vlz9}Ugcg`CgBFte&hOO=G3{DB_=DT%*-)OQyI_qzE9+b z_6z3pX2x~WpBdk1iDJ`&MKz|<_9$Nz-ViZaTre}o$&U!9k558N)tY4+aA^1c3 zsOI~sKgt({H%v?dU<>Un9qV@!)AmGe@Z}&V>3lS>Lwn*!EVi1;)zoB3`eWjic}>$F z$H^Q?IGM`xXRlK`97bdTZcPbc$3^75!CscUpl?68Kbr32T_QQP*W6ZY`i z@^T5A7ho+DlPz7-q3^{jVkDWfc1gf|-|NS*#ixVdVLg=FsMtR+Gw;0@<`1xziAjK5 z()x`QKdqGaRni{Zhf9~cx{Q9*TNWp^`d$k%)%UR<_!SY8Tuv%`5+CyiQD5(1i$(>T zN@a$+*Im5{M4za>cIhnquK1)u)qkO!Qyqo>V$SNcRL)A4m3IVhj$O$7rB2#6Oxgrf zXWr?3?H|eVD4)=Oo;-5{zOaYzUHf<{Wy${#p>5TO1<~aS(cHxcpvVqjn{hxk!QKF} x8n(BDn*0f;V8l*mpklkV6=*BaR$#>n{0A}*TvCR#e2M@7002ovPDHLkV1hPx&`$5=Akt3lWJ2BbuW^Jcy!@ zAYPOlBIw27!Gi=P5yXQ^lpunjf|#fmMMTg@axfkw;3YWIUEQ;5Z`bpyS!-#A?%ti* z^)?N=uip2nUVZyj)vH>f={Hqis=!o%|3?84@%l@1IC;?FznV1F6gY}?ytf_|FrDT@3{^mj+cFw;+?*k*laBMGp4zGykc z%tu{zA0~(2vExAMya;W#{#Mz>r8I=%bnp~N9cGr_+hOn^$N`oJ`ztmi9G4Fu{u&W~ z?6P-yh4O7I`~pH3+o6BN^q+)mBQb33Gw2Wq@u_a)e`F^}lEFeD#aZJ!6wGT*KOmuf zHf}A{y+4ZP&RKsP+qlO->Tdki+woTy<)k2WkMV*36@_%ApU|Fi+_r^_o6(+#SmPeU z8srI4*G^S zpXs42%Whor$7nPXBS+rXEQc%dU1Km|N_0^~74ad2L*nF`4%Wq+@J3>tF#G~1Ea<7F z2W%2dW)50rd?wMZ_Lgj{(rQ^7TN$qb8;O}Q;?i@Zi??Hup-P68Lo~9Wv`=I7*tJS(vlY0oG_|7XO*ERL2 zimgk`4C+R~Tc}vCEU)NqW2Fum4CKzcjywb96zaxhgVlWnrMv24b@+yenZcdN|BzZ|N|o7N@ggl$(@ z*7^~1LaSnR*jSxS5-i=|D$jXbv_95bS{gooqR2l3e5hj8L^ve3CRui5qL7{aBpViBqa=P_ zwDD03;xiz9B35|y#_DzW{%D`FKe1va$?A(XKgZ-AXt(FS!(<#M*$;T(u3Nis-@XgA zpDdFpwM9cmTZD4MX!kMCG?-#B6p`LO9p^!D6?t z+<*zLM{FJ^*g>sX#>xENWA9iP&i-P@Ozl*GsRC04Hle`3A9r|4HJt@c00000NkvXX Hu0mjfkXR9T literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--error.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/icon-shoes--error.png new file mode 100755 index 0000000000000000000000000000000000000000..48b07d4540f1f38ed4259ccd55be3e9e947f6184 GIT binary patch literal 1400 zcmV-;1&8{HP)Px)GD$>1R9Fe^S8GgMMHD_WcY#WU(g(IhOAyrlP?LsOV-1QVwvDw~LkubIni^Vn zm&cNrMx$vhv29}XSHnYCfi|{)gf>mo_yZGz(Zr}RMx~fiEwoUT)L;v?uHtGuqac|C&yz!n4$BtM!9Lwjf@7Jxxr zfj~Er1WJr+pl!VyE_}nf*db#Dc6?(?=L5a{m(CKXmLLCpMfuLe1DKDVoiOUF2~y!tX_qMG(QU*&OaB_P<^`cc~T;P^N@6dLRRCEg)N zr>NAs=tGZ-PDi3dotY#= z5uqC){aTQC&FqkA1~*g?=?6;Ltj>&p8$MtMihbM^n9p1KJtgg3H97HRW8;T8Y~=O2 zei54XBt;`dq$iEC%w}XZH+QVI(Z^NY6c}6iysjoG#i5YUzp8C)e~D#P?ic!`l-|mc zl56xP_?=}|BY?QUN@pdX7ce%iayvU6$)b>=R8ia1R?o6ZxARAy=@354v(-SQimY^2 z@_7Se2Nh+`fi)|W3#q;~)HSxha>J>3?*<)*Lh&3m&4Tt?&|==Qq@-lhftrgNtj6!} z%wt)#*Lhk<2uG3mxdYcXG)GEct}ec0!NAx}d3ostI{uGLVQ2=->g6e~(ZMzpe;Z1- z!lrtEdpTb;t=5?3l$FJeuCA`zM*a@|MEBJCYLBy)z0@|HD5Zkyu=7Y8UZx}CM3P)~ zz~gzwI1lHTEGlB`g;Sd3a1>MIY8w0<1uXMPx(FG)l}R9Fe^R$Yr*MHrrUCdsBr&NDp;sxZz_m_tREMOAc7aY z(|@slqgPtN3-?lrUZ^ds7Q_WXYXm`5uw6Hs@8mSu4G;I2s7`; zJoB62E@Yh)ghkmW=<0Sf$fhjkxN4r4%QXM!p9H`)9;)0>(s&6^q4K zM9Mc^R5v3G%Lx2AhVZ;z9~mn%#my7oG6E4wOcPpLJ9kkrbs3ecXH9dD)S`b6_ZuyH zKuiIC69xHlquqWf0#NKoX)G|YDVeM;n&y`}e`Li8jZk8z*Ur4fn2IWnUuO-g=wUV* zN2eKoV7Z;n-{MrNMcQ3)=oh7cA{~M8^OW~1W&P#No2GvV?>~h$)0$}}Kg}A(F)5vi z!dcfnsbS~y`3|C5)%Jf{Owl2kt%VY!|3J~Mj*mkNq&(St_@o7K* zB)(8$1pQ~MYU@%k(iCHP?V#P(Q;GJek>Iewp-`*UtYO!r<4|H8ojW3im&d_Kp#d2e zu^$y8HXiqEDlHI+#4mXEG<_&B%d&n4CZe>7&wHSBBB_X>qisQhxKgec^XsX^9MAQO z2l+yY=}B(C`vrpg#y(;~+xqw)b?`d%HgZV0uG`oCpvFP11jWMqk0h4M5=^umQ!kbJB)B)jpDi~Xy&omQX(V1;} z_IQsVUMKRG^h0wz=4a2OmC8{de;_F`A(zJ z&X>yvx6_7kla%?64#@>3#91n`tAg{KewJ%2iuE{Wf%{SC%)H0+)T|A62JU%gE9%!@K%+L*A5DHcp z=LY9!5TPVJYk|npg9Puz1l&rcQYCNByq_yDS75HdV<_-14H=T6zfwzo00000NkvXX Hu0mjf`QanO literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/localization/en.json b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/localization/en.json new file mode 100755 index 0000000..32f2bcb --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/localization/en.json @@ -0,0 +1,401 @@ +{ + "GPS": "GPS", + "start_workout": "START WORKOUT", + "my_workouts": "My Workouts", + "all_activities_title": "All Activities", + "all_activities_list_item": "All Activities", + "activity_history_name_on": "on", + "select_exercise_menu_item": "Select Activity", + "my_challenges": "My Challenges", + "select_exercise_list_title": "Select Activity", + "workout_finish": "Finish", + "mi_header": "mi", + "m_per_s": "(m/s)", + "workout_save": "Save", + "workout_discard": "Discard", + "workout_discard_confirmation": "Are you sure you\nwant to discard\nthis workout?\n\nAll data will\nbe lost.", + "workout_cancel_discard": "Cancel", + "workout_pause": "Pause", + "workout_resume": "Resume", + "workout_with_another_app": "A workout is already active in another app.", + "workout_save_confirmation_title": "Your workout has been saved.", + "workout_save_confirmation_subtitle": "Allow a few minutes for workout to sync across all devices.", + "workout_summary": "Workout\nSummary", + "workout_no_network_connection_title": "No network\nconnection.", + "workout_no_network_connection_subtitle": "Workout will sync when connection is restored.", + "login_request": "Please log in to MapMyRun on your phone.", + "login": "LOG IN", + "install_request": "Please install MapMyRun on your phone.", + "install": "INSTALL", + "network_request": "No Network Connection", + "bluetooth_request": "No Bluetooth Connection", + "try_again": "TRY AGAIN", + "km_header": "km", + "button_ok": "OK", + "profile_data_description": "We need a few more stats from you to calculate calorie burn.", + "profile_data_description_standalone": "To estimate\ncalorie burn,\nplease enter your\nheight and weight\nin the MapMyRun\napp on your\nphone.", + "profile_data_complete_on_phone": "COMPLETE ON PHONE", + "profile_data_skip": "SKIP", + "profile_data_redirected_description": "It may take several minutes for Gear to reflect these changes.", + "profile_data_skipped_description": "To edit these stats later, open MapMyRun on your phone and go to Edit Profile.", + "workout_didnt_sync": "Workout didn't sync. No network connection.", + "workout_has_synced": "Your MapMyRun workout has synced.", + "workout_hrs": "hrs", + "workout_min": "min", + "workout_min_per_mi": "min/mi", + "workout_min_per_km": "min/km", + "workout_mi": "mi", + "workout_km": "km", + "workout_mi_per_hr": "mph", + "workout_km_per_hr": "km/h", + "workout_save_or_discard": "Save or Discard this workout.", + "workout_paused": "Paused", + "settings": "Settings", + "feedback": "Feedback", + "haptic_feedback": "Vibration", + "voice_feedback": "Voice", + "voice_feedback_on": "On", + "voice_feedback_off": "Off", + "voice_feedback_distance": "Distance", + "voice_feedback_duration": "Duration", + "voice_feedback_avg_pace_speed": "Avg. Pace/Speed", + "voice_feedback_current_pace_speed": "Current Pace/Speed", + "voice_feedback_split_pace_speed": "Split Pace/Speed", + "voice_feedback_cadence": "Cadence", + "voice_feedback_stride_length": "Stride Length", + "voice_feedback_current_hr": "Current HR", + "settings_min": "min", + "settings_mi": "mi", + "settings_km": "km", + "settings_interval": "Interval", + "done": "done", + "gps_off_notification": "Maps unavailable.\nEnsure GPS is\nactive on your\nphone and\nyour Gear.", + "map_acquiring_gps": "Attempting\nto acquire\nGPS signal.", + "pairing_error": "To complete\npairing, tap\n¡°Accept¡± on your phone.", + "update_fw": "Please update\nyour Gear\nfirmware then\nre-install the app.", + "wko_resume_error": "Unable to resume\nworkout.\n\nYour workout\nhas been saved.", + "awards": "Awards", + "top": "Top #%", + "zero_day_left": "# days left", + "one_day_left": "# day left", + "two_day_left": "# days left", + "few_day_left": "# days left", + "many_day_left": "# days left", + "other_day_left": "# days left", + "network_offline": "Network offline.\n\nTry again when\nnetwork connection\nis restored.", + "challenges_list_empty": "No challenges\nyet.\n\nUse your\nphone to find\na challenge.", + "map_unable_to_acquire_gps": "Unable\nto acquire\nGPS signal.\n\nPlease move\nto open space.", + "rate_app_question": "Do you like using\nMapMyRun for\nSamsung Gear?", + "post_review_question": "Great! Would you\ntake a moment\nto post a review?", + "yes": "YES", + "no": "NO", + "no_workout_stats": "This workout\ndoes not have\nany stats.", + "workouts_list_empty": "No workouts yet.", + "this_week": "This week", + "last_week": "Last week", + "last_thirty_days": "Last 30 days", + "older": "Older", + "group_class": "Class", + "group_cycling": "Cycling", + "group_dance": "Dance", + "group_gym": "Gym", + "group_martial_arts": "Martial Arts", + "group_other": "Other", + "group_run": "Run", + "group_sport": "Sport", + "group_swim": "Swim", + "group_walk": "Walk", + "group_winter": "Winter", + "group_yoga": "Yoga", + "activity_id_1": "Generic Workout", + "activity_id_7": "Fartleks", + "activity_id_9": "Walk", + "activity_id_10": "Winter Sport / Activity", + "activity_id_16": "Run", + "activity_id_19": "Indoor Bike Ride", + "activity_id_20": "Swim", + "activity_id_21": "Other Sport", + "activity_id_22": "Rock Climbing", + "activity_id_23": "Class Workout", + "activity_id_24": "Hike", + "activity_id_26": "Total Body", + "activity_id_28": "Circuit Training", + "activity_id_29": "Sailing", + "activity_id_31": "Pilates", + "activity_id_33": "Fixed Gear (Fixie)", + "activity_id_34": "Aerobics", + "activity_id_36": "Road Cycling", + "activity_id_41": "Mountain Biking", + "activity_id_43": "Yard Work", + "activity_id_44": "Track", + "activity_id_45": "Triathlon", + "activity_id_47": "Unicycling", + "activity_id_54": "Volleyball", + "activity_id_56": "BMX", + "activity_id_57": "Canoeing", + "activity_id_59": "Class / Aerobics", + "activity_id_60": "CycloCross", + "activity_id_65": "Boxing", + "activity_id_68": "Softball", + "activity_id_71": "Brick", + "activity_id_72": "Other Martial Arts", + "activity_id_73": "Tennis", + "activity_id_74": "Snow Skiing", + "activity_id_75": "Lap", + "activity_id_77": "House Work", + "activity_id_80": "Snorkeling", + "activity_id_82": "Ballet", + "activity_id_84": "Wakeboarding", + "activity_id_85": "Fishing", + "activity_id_86": "Ice Skating", + "activity_id_94": "Wind Surfing", + "activity_id_95": "Skateboarding", + "activity_id_96": "Nordic Track", + "activity_id_99": "Rowing Machine", + "activity_id_101": "Roller Skating", + "activity_id_102": "Commute", + "activity_id_103": "Sprints", + "activity_id_104": "Curling", + "activity_id_105": "Power", + "activity_id_107": "Snowboarding", + "activity_id_108": "Track", + "activity_id_111": "Water", + "activity_id_115": "Time Trial", + "activity_id_116": "Bowling", + "activity_id_119": "Snowshoeing", + "activity_id_120": "Spin Class", + "activity_id_121": "Water Polo", + "activity_id_123": "Bootcamp", + "activity_id_124": "Group", + "activity_id_127": "Surfing", + "activity_id_128": "Rowing", + "activity_id_131": "Wrestling", + "activity_id_133": "Stairs", + "activity_id_134": "Baseball", + "activity_id_135": "Hockey", + "activity_id_137": "Horseback Riding", + "activity_id_138": "Whitewater Rafting", + "activity_id_141": "Ice Hockey", + "activity_id_142": "Badminton", + "activity_id_148": "Cheerleading", + "activity_id_153": "Ultimate", + "activity_id_154": "Golf", + "activity_id_155": "Fencing", + "activity_id_156": "Water Skiing", + "activity_id_161": "Field Hockey", + "activity_id_163": "Squash", + "activity_id_164": "Yolates", + "activity_id_169": "Inline Skating", + "activity_id_171": "Skydiving", + "activity_id_172": "Race/Event", + "activity_id_176": "Football / Soccer", + "activity_id_178": "Lacrosse", + "activity_id_180": "Open Water", + "activity_id_182": "Rollerskiing", + "activity_id_184": "Scuba Diving", + "activity_id_192": "Water Aerobics", + "activity_id_193": "Group", + "activity_id_197": "Intervals", + "activity_id_199": "Calisthenics", + "activity_id_200": "Gardening", + "activity_id_201": "Adventure Race / Event", + "activity_id_204": "Dog Walk", + "activity_id_205": "Kitesurfing", + "activity_id_208": "Treadmill", + "activity_id_214": "Physical Therapy", + "activity_id_216": "Soccer, Indoor", + "activity_id_221": "Bouldering", + "activity_id_222": "Floorball", + "activity_id_224": "Farming", + "activity_id_227": "Hill Workout", + "activity_id_228": "Racquetball", + "activity_id_230": "Workout Video", + "activity_id_235": "Rugby", + "activity_id_250": "Elliptical", + "activity_id_251": "Kickball", + "activity_id_254": "Gymnastics", + "activity_id_257": "Kayak", + "activity_id_258": "Netball", + "activity_id_259": "Basketball", + "activity_id_263": "Jump Rope", + "activity_id_264": "Hunting", + "activity_id_266": "Trail", + "activity_id_267": "Wood Chopping", + "activity_id_271": "Cricket", + "activity_id_275": "Nordic, Walk", + "activity_id_279": "Indoor", + "activity_id_284": "Diving, Board", + "activity_id_336": "Beach Volleyball", + "activity_id_468": "Tai Chi", + "activity_id_469": "Judo", + "activity_id_471": "Karate", + "activity_id_472": "Kickboxing", + "activity_id_473": "Aikido", + "activity_id_498": "Bikram", + "activity_id_499": "Vinyasa", + "activity_id_500": "Yoga", + "activity_id_546": "Indoor Trainer", + "activity_id_548": "Stationary Bike", + "activity_id_564": "Sparring", + "activity_id_598": "Upper Body", + "activity_id_622": "Dance Class", + "activity_id_627": "Step Aerobics Class", + "activity_id_633": "Race", + "activity_id_637": "Commute", + "activity_id_704": "CrossFit Class", + "activity_id_708": "Motocross", + "activity_id_710": "ElliptiGo", + "activity_id_714": "Longboarding", + "activity_id_722": "Hot", + "activity_id_724": "Power", + "activity_id_726": "Ashtanga", + "activity_id_728": "Ergometer", + "activity_id_730": "Stair Machine", + "activity_id_732": "Basic Training", + "activity_id_740": "Tae Bo", + "activity_id_742": "Ballroom", + "activity_id_746": "Hip Hop", + "activity_id_748": "Hula", + "activity_id_750": "Latin", + "activity_id_752": "Salsa", + "activity_id_754": "Zumba", + "activity_id_756": "Indoor Track", + "activity_id_758": "Handball", + "activity_id_760": "Jujitsu", + "activity_id_762": "Kung Fu", + "activity_id_764": "Tae Kwon Do", + "activity_id_768": "American Football", + "activity_id_813": "Mixed Martial Arts", + "activity_id_819": "Table Tennis", + "activity_id_825": "Stroller - Single", + "activity_id_827": "Stroller - Double", + "activity_id_829": "Stroller - Single", + "activity_id_831": "Stroller - Double", + "activity_id_845": "Trampoline", + "activity_id_855": "Dog Run", + "activity_id_861": "Jacobs Ladder", + "activity_id_863": "Stand Up Paddling", + "activity_id_880": "Muay Thai", + "activity_id_882": "Barre Workout", + "activity_id_890": "Legs", + "select_stat": "Select Stat", + "label_distance": "Distance", + "label_pace": "Pace, Current", + "label_pace_avg": "Pace, Average", + "label_pace_max": "Pace, Max", + "label_speed": "Speed, Current", + "label_speed_avg": "Speed, Average", + "label_speed_max": "Speed, Max", + "label_calories": "Calories", + "label_hr": "Heart Rate, Current", + "label_hr_avg": "Heart Rate, Average", + "label_hr_max": "Heart Rate, Max", + "label_intensity": "Intensity", + "label_willpower": "WILLPOWER¢â", + "label_duration": "Duration", + "label_distance_medium": "DISTANCE", + "label_pace_medium": "PACE", + "label_pace_avg_medium": "AVG PACE", + "label_pace_max_medium": "MAX PACE", + "label_speed_medium": "SPEED", + "label_speed_avg_medium": "AVG SPEED", + "label_speed_max_medium": "MAX SPEED", + "label_calories_medium": "CALORIES", + "label_hr_medium": "HR", + "label_hr_avg_medium": "AVG HR", + "label_hr_max_medium": "MAX HR", + "label_intensity_medium": "INTENSITY", + "label_willpower_medium": "WILLPOWER", + "label_duration_medium": "DURATION", + "label_hr_zone_medium": "ZONE", + "label_distance_short": "DIST", + "label_pace_short": "PACE", + "label_pace_avg_short": "AVG PACE", + "label_pace_max_short": "MAX PACE", + "label_speed_short": "SPEED", + "label_speed_avg_short": "AVG SPEED", + "label_speed_max_short": "MAX SPEED", + "label_calories_short": "CAL", + "label_hr_short": "HR", + "label_hr_avg_short": "AVG HR", + "label_hr_max_short": "MAX HR", + "label_intensity_short": "INT", + "label_willpower_short": "WILLPWR", + "label_duration_short": "DUR", + "cal": "cal", + "bpm": "bpm", + "stat_ux_tip": "Tap on screen to rotate stats.\nLong-press on a stat to swap.", + "map_ux_tip": "Tap on screen to show stats.", + "consent_error": "Please open\nMapMyRun on\nyour phone to\naccept updated\nterms and privacy\npolicy.", + "consent_waiting": "Open MapMyRun\non your phone to\ncontinue.", + "workout_steps_per_min": "spm", + "workout_meter": "m", + "workout_inch": "in", + "workout_centimeter": "cm", + "label_cadence": "Cadence, Current", + "label_cadence_avg": "Cadence, Average", + "label_stride_length": "Stride length, Current", + "label_stride_length_avg": "Stride length, Average", + "label_steps": "Total Steps", + "label_cadence_medium": "CADENCE", + "label_cadence_avg_medium": "AVG CADENCE", + "label_stride_length_medium": "STRIDE", + "label_stride_length_avg_medium": "AVG STRIDE", + "label_steps_medium": "STEPS", + "label_cadence_short": "CAD", + "label_cadence_avg_short": "AVG CAD", + "label_stride_length_short": "STRIDE", + "label_stride_length_avg_short": "AVG STRIDE", + "label_steps_short": "STEPS", + "select": "SELECT", + "wko_details_cardio": "Heart Rate", + "wko_details_form": "Form", + "goal_complete": "GOAL COMPLETE", + "target_goal": "Target: {{goal}}", + "form_coaching_edu_header_form": "Find Your\nForm", + "form_coaching_edu_header_cadence": "Find Your\nCadence", + "form_coaching_edu_screen_text_form": "Achieving your optimal\nrunning form can make\nrunning feel easier, better\nmanage your risk of injury,\nand improve your\nperformance.\n\nThe Form Coaching\nfeature will monitor your\nCadence during your run,\nalerting you if you\nfall outside your\n ideal range.Your\nrange varies and is\ncalculated by your\nrunning stats, which can\nchange throughout your run.\n\nIf you fall outside of\nyour target range, try\nincreasing or decreasing\nyour step rate while\nmaintaining the same pace.", + "form_coaching_edu_screen_text_cadence": "Understanding your\nrunning form helps\nimprove your overall run.\nOne factor that can\naffect your workout\nis Cadence, measured\nby the total number of\nsteps you take per minute.\nYour target range\nis based on a\nvariety of factors\nincluding age, height,\nweight, gender, and pace.\n\nWe¡¯ll let you know\nwhen you¡¯re below\nor above your target\nrange during your workout.\n\nTo get a deeper\nrunning form breakdown,\nincluding personalized\nform coaching tips,\nview this workout\nin the MapMyRun app\non your mobile phone.", + "form_coaching_edu_do_not_show": "Don't show again", + "goal_finished": "GOAL!", + "workout_setup": "Workout Setup", + "goal_item": "Goal", + "goal_setup": "What's your\ngoal today?", + "goal_basic_workout": "Basic Workout", + "goal_distance": "Distance", + "goal_duration": "Duration", + "goal_pace": "Pace", + "split_updates": "Split Updates", + "goal_coaching": "Goal Feedback", + "form_coaching": "Form Coaching", + "split_updates_no_feedback": "No Feedback", + "split_updates_audio_frequency": "Frequency", + "split_updates_audio": "Audio", + "split_updates_audio_desc": "Audio update of\nworkout stats", + "split_updates_haptic": "Vibration", + "split_updates_haptic_desc": "Vibrate at set frequency", + "form_coaching_no_feedback": "No Feedback", + "form_coaching_audio": "Audio", + "form_coaching_audio_desc": "Receive audio alerts about your running form", + "form_coaching_haptic": "Vibration", + "form_coaching_haptic_desc": "Vibrate when Cadence is\nout-of-range", + "form_coaching_display": "Display", + "form_coaching_display_desc": "Auto-transition to the\nForm Coach screen", + "goal_coaching_no_feedback": "No Feedback", + "goal_coaching_audio": "Audio", + "goal_coaching_audio_progress_desc": "Alert at 25% intervals", + "goal_coaching_audio_range_desc": "Receive audio guidance when out-of-range", + "goal_coaching_haptic": "Vibration", + "goal_coaching_haptic_progress_desc": "Vibrate at 25% intervals", + "goal_coaching_haptic_range_desc": "Vibrate when out-of-range", + "voice_feedback_avg_speed": "Avg. Speed", + "voice_feedback_current_speed": "Current Speed", + "voice_feedback_split_speed": "Split Speed", + "voice_feedback_avg_pace": "Avg. Pace", + "voice_feedback_current_pace": "Current Pace", + "voice_feedback_split_pace": "Split Pace", + "today_workout": "Today's Workout", + "coaching_warm_up": "Warm Up", + "debug_settings": "Debug Settings", + "debug_logging": "Debug Log" +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/logo--tray.png b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/res/logo--tray.png new file mode 100755 index 0000000000000000000000000000000000000000..4882ffc0541fb3aec374bffc0afc1340b5379205 GIT binary patch literal 2112 zcmV-G2*3A?00001b5ch_0Itp) z=>Px+`bk7VRCodHoOz5CMHGiw4!Pwt(L~XxyMW3ipdjJ}ni#ww%Bhkd{Gn)qi6O>| z7%$K$U`$lh=%R~&lE@)yh!RaakVTYmgDZ)GKxBbMkbC`plkG{*>>Sn8RlPgxyyV-y zs@{85udBPNx@Lb9sI0R5p4^nhhWMMaOX%{!qyyK%Y#)gAC|zwP-3RZVJe(x17lzTR6#bG<8463`6oi$Tp&hCt1r+?hu!Sf8Kky=)AE+2)XbShkdax-CJs@6Po?u6h z{GO=-)7U#n0tI3s$J?P4F88h#wpMGPDcZ)sZ`8MXX}$g0V*%mh9h}?<@4$ur1Qz$} zXP@1yfUbPM9rM3X|78$+lr?5&5hoXen;wB#fg5tFA__kiHW7>AY9W0aa%~uv-Bk3b{knfLdUg@~eeNX27A?p1VI$G<#RxyY~Gf8b<70 z9t#KudSt!isx$??8EfDOSD)}wcr8T7nrm{|1#adU&BLur-(R3X{N>e2JRqDr%E=LM z*t=+X+tISVNYpWUZiS)nQ|GC@UC!z0{!8(I=p;eez-EF!fyMnxEb59K(pCmEtFHnlWa|7q)DOxX?BwKChP})UsIULVB>2cYGU$zI>))%8g7MG zKvUAE!IfxHhQ5SOfu5k2S4%kG18hnK%JlTfT~MIUxO**nHQe(Coo!(psB%&*wc&Im zqO}6EKx@h3!tqxYt$&WLtR0(_cShW!njIABHh}oU7M@lM)@#sDXajOvKZxhRGQw^M!qW% zIy-yAtUe*90ZqAiSpN>m(7BpPrqYPXGFb3+cy&` z6XZvZQZ5(GR3_tYCCW9pDut^uDKxyMi9pjXp?23jy z)HOo)M(h5Ox<=_jBlX^38UT_u+6|w;e9$NDHDKx+Nz=*?Tp3AFcBV~Xa)Cf-bgnrb zTcSRHSHEqJJHjOR0{(@l_<9fT^_1^#a#8?mo*#)1zYka)athU^Ko>}mpj z%*~#Z6gZ%}^lPv!>Zhl?0gN0=lL(#qj-qaOXHWpG3Ody*vn}f0-)H0~2{jIIA|51%NCyDYqyb*rI70nuDh zK{D4sR5w$x0-_$4zAY4_ZfoFM%+J-2KHq0Ytz0b(gvD-=*)r8HKikqj-LfS!(_5_HSEvA+AE{QM zPgDBI-*K{60-0KZzO1ce1b#V_0#ZT2=E4J@k4)CYS^pYGb*FTPD?!Wjwc?|c qZQcoGc^9X(IBy-Sp&WZ%72s00001b5ch_0Itp) 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 + + + + + MapMyRun.png + + + + + + + + http://tizen.org/privilege/appmanager.launch + + + + \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/CircleProgressBarSurfaceItemExtension.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/CircleProgressBarSurfaceItemExtension.cs new file mode 100755 index 0000000..c4c873c --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/CircleProgressBarSurfaceItemExtension.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; + +namespace MapMyRun.Tizen.Views +{ + static class CircleProgressBarSurfaceItemExtension + { + const string CircleProgressBarAnimationName = "CircleProgressBarProgressTo"; + + public static void ProgressTo(this CircleProgressBarSurfaceItem item, double toValue, uint duration) + { + var tcs = new TaskCompletionSource(); + var circlePage = item.Parent as CirclePage; + var weakRef = new WeakReference(item); + + new Animation(v => { + if (weakRef.TryGetTarget(out var pg)) + { + pg.Value = v; + } + }, item.Value, toValue, Easing.SinOut).Commit(circlePage, CircleProgressBarAnimationName, 16, duration, finished: (f, a) => tcs.SetResult(a)); + } + + public static void ProgressTo(this CircleProgressBarSurfaceItem item, double fromValue, double toValue, uint duration) + { + item.Value = fromValue; + item.ProgressTo(toValue, duration); + } + + public static void CancelAnimation(this CircleProgressBarSurfaceItem item) + { + var circlePage = item.Parent as CirclePage; + circlePage.AbortAnimation(CircleProgressBarAnimationName); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml new file mode 100755 index 0000000..4e55cd9 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml.cs new file mode 100755 index 0000000..5a33546 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/LoadingPage.xaml.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xamarin.Forms; +using Xamarin.Forms.Xaml; +using Tizen.Wearable.CircularUI.Forms; +using MapMyRun.Tizen.ViewModels; +using MapMyRun.Tizen.Models; +using MapMyRun.Models; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class LoadingPage : CirclePage + { + private readonly Loading _loading; + public static readonly BindableProperty ProgressProperty = + BindableProperty.Create("Progress", + typeof(double), + typeof(CircleProgressBarSurfaceItem), + propertyChanged: (b, o, n) => + ProgressBarProgressChanged((CircleProgressBarSurfaceItem)b, (double)n)); + + public LoadingPage(Loading loading) + { + InitializeComponent(); + _loading = loading; + BindingContext = new LoadingPageModel(_loading); + ProgressBar.SetBinding(ProgressProperty, "Progress"); + } + + private static void ProgressBarProgressChanged(CircleProgressBarSurfaceItem progressBar, double progress) + { + progressBar.CancelAnimation(); + progressBar.ProgressTo(progress, 200); + if (progress >= 1) + { + Device.StartTimer(TimeSpan.FromSeconds(0.3), () => + { + Application.Current.MainPage = new NavigationPage(new MainPage()); + return false; + }); + } + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml new file mode 100755 index 0000000..ef9771f --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml.cs new file mode 100755 index 0000000..7f9a4e9 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/MainPage.xaml.cs @@ -0,0 +1,168 @@ +using System; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; +using Tizen.Wearable.CircularUI.Forms; +using MapMyRun.Tizen.ViewModels; +using System.Timers; +using MapMyRun.Models; +using MapMyRun.Models.Workout; +using MapMyRun.Models.Settings; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class MainPage : CirclePage + { + private WorkoutSetupPageModel setupPageModel; + private Timer gpsAnimation; + private MainPageModel mainPageModel; + + private double MIN_SCALE = 0.7; + private double MAX_SCALE = 1.0; + private bool Initialized = false; + + public MainPage() + { + mainPageModel = new MainPageModel(); + setupPageModel = new WorkoutSetupPageModel(); + + InitializeComponent(); + + modifyBtn.BindingContext = setupPageModel; + statusTray.BindingContext = mainPageModel; + + scroller.Scrolled += OnScrolled; + scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Never; + AbsoluteLayout.SetLayoutFlags(listBg, AbsoluteLayoutFlags.SizeProportional); + + Appearing += (s, e) => + { + // Only initialize black background on first draw + if(!Initialized) + { + Initialized = true; + Moved(0); + } + ToggleGpsAnimation(true); + }; + + Disappearing += (s, e) => + { + ToggleGpsAnimation(false); + }; + + TapGestureRecognizer modifyTapGesture = new TapGestureRecognizer(); + modifyTapGesture.Tapped += OnModifyTapped; + modifyBtn.GestureRecognizers.Add(modifyTapGesture); + + TapGestureRecognizer startTapGesture = new TapGestureRecognizer(); + startTapGesture.Tapped += OnStartTapped; + startBtn.GestureRecognizers.Add(startTapGesture); + + TapGestureRecognizer historyTapRecognizer = new TapGestureRecognizer(); + historyTapRecognizer.Tapped += OnHistoryTapped; + myWorkoutsBtn.GestureRecognizers.Add(historyTapRecognizer); + + TapGestureRecognizer settingsTapRecognizer = new TapGestureRecognizer(); + settingsTapRecognizer.Tapped += OnSettingsTapped; + settingsBtn.GestureRecognizers.Add(settingsTapRecognizer); + } + + async void OnModifyTapped(object sender, EventArgs args) + { + await Navigation.PushAsync(new WorkoutSetupPage(setupPageModel)); + } + + async void OnStartTapped(object sender, EventArgs args) + { + // TODO : Move proper position + Workout workout = (Application.Current as App).workout; + workout.PrepareWorkout(16, true, true, false); + await Navigation.PushAsync(new WorkoutMainLoadingPage()); + } + + async void OnHistoryTapped(object sender, EventArgs args) + { + await Navigation.PushAsync(new WorkoutHistoryPage()); + } + + async void OnSettingsTapped(object sender, EventArgs args) + { + await Navigation.PushAsync(new SettingsPage()); + } + + private void ToggleGpsAnimation(bool enabled) + { + if (enabled) + { + gpsAnimation = new Timer(1000); + gpsAnimation.AutoReset = true; + int nextGpsState = 1; + gpsAnimation.Elapsed += (se, ev) => + { + if (mainPageModel.GpsState == GPSState.Acquiring) + { + gpsStatus.FadeTo(nextGpsState, 1000); + nextGpsState = nextGpsState == 1 ? 0 : 1; + } else + { + gpsStatus.Opacity = 1; + } + }; + + gpsAnimation.Start(); + } + else + { + gpsAnimation?.Stop(); + gpsStatus.Opacity = 1; + } + } + + void OnScrolled(object sender, ScrolledEventArgs e) => Moved(e.ScrollY); + + protected override bool OnBackButtonPressed() + { + var request = new Request(OperationType.ExitApp, ""); + var dispatcher = MapMyRun.Models.Dispatcher.Instance; + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Receive Exit App Signal Response"); + }; + dispatcher.SendRequest(request); + Console.WriteLine("Sent Exit App Signal"); + + return false; + } + + void Moved(double y) + { + double SCREEN_SCROLLER_OFFSET = 16; + AbsoluteLayout.SetLayoutBounds(listBg, new Rectangle(0, scroller.Y + startBtn.Y + startBtn.Height / 2 - y - SCREEN_SCROLLER_OFFSET, 1.0, 2)); + for (int i = 1; i < fETarget.Children.Count; i++) + { + var view = fETarget.Children[i]; + + double distanceFromCenter = y; + switch (i) + { + case 1: // Start button + distanceFromCenter = y; + break; + case 2: // My Workouts + distanceFromCenter = Math.Abs(y - 150); + break; + case 3: // Settings + distanceFromCenter = Math.Abs(y - 250); + break; + } + double scale = MAX_SCALE - (distanceFromCenter / 750); + + view.Scale = scale < MIN_SCALE ? MIN_SCALE + : scale > MAX_SCALE ? MAX_SCALE + : scale; + } + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml new file mode 100755 index 0000000..9ada330 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml.cs new file mode 100755 index 0000000..ea62f88 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SelectWorkoutPage.xaml.cs @@ -0,0 +1,216 @@ +using MapMyRun.Models; +using MapMyRun.Tizen.Models; +using MapMyRun.Tizen.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class SelectWorkoutPage : CirclePage + { + private double MIN_SCALE = 0.6; + private double MAX_SCALE = 1.1; + private int SCROLL_INTERVAL = 144; + + private WorkoutSetupPageModel setupPageModel; + private bool Initialized = false; + private double lastScrollPos = 0; + private int focusedIndex = 0; + private bool isTimerRunning = false; + private bool isScrolling = false; + + private void Initialize(WorkoutSetupPageModel _setupPageModel) + { + InitializeComponent(); + + setupPageModel = _setupPageModel; + BindingContext = setupPageModel; + scroller.Scrolled += OnScrolled; + scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Never; + + Appearing += (s, e) => + { + if (!Initialized) + { + Initialized = true; + Scroller(0); + } + }; + + TapGestureRecognizer selectTapGesture = new TapGestureRecognizer(); + selectTapGesture.Tapped += OnSelectTapped; + selectBtn.GestureRecognizers.Add(selectTapGesture); + } + + public SelectWorkoutPage(WorkoutSetupPageModel _setupPageModel) + { + Initialize(_setupPageModel); + UpdateActivityList(setupPageModel.defaultList); + } + + public SelectWorkoutPage(WorkoutSetupPageModel _setupPageModel, int group_id) + { + Initialize(_setupPageModel); + + if (group_id == -1) // Others + { + UpdateActivityList(setupPageModel.GetGroupActivities()); + } + else + { + UpdateActivityList(setupPageModel.GetActivitiesByGroup(group_id)); + } + } + + private Label LabelFactory(Activity activity) + { + + ActivityLabel label = new ActivityLabel(activity); + label.Text = activity.name; + label.HeightRequest = 137; + label.VerticalOptions = LayoutOptions.Start; + label.HorizontalOptions = LayoutOptions.CenterAndExpand; + + TapGestureRecognizer activityTapGesture = new TapGestureRecognizer(); + activityTapGesture.Tapped += Activity_Selected; + label.GestureRecognizers.Add(activityTapGesture); + + return label; + } + + private void UpdateActivityList(List activityList) + { + activityStack.Children.Clear(); + activityStack.HeightRequest = activityList.Count * 148; + + activityList.ForEach((Activity activity) => + { + activityStack.Children.Add(LabelFactory(activity)); + } + ); + } + + void OnSelectTapped(object sender, EventArgs args) + { + Activity focusedActivity = (activityStack.Children[focusedIndex] as ActivityLabel).LabelActivity; + + setupPageModel.updateCurrentActivity(focusedActivity); + PopSelectPages(); + } + + async private void Activity_Selected(object sender, EventArgs e) + { + Activity tappedActivity = ((ActivityLabel)sender).LabelActivity; + + if (tappedActivity.id == -1) // Not an activity but a holder + { + await Navigation.PushAsync(new SelectWorkoutPage(setupPageModel, tappedActivity.group_id)); + } + else + { + setupPageModel.updateCurrentActivity(tappedActivity); + PopSelectPages(); + } + } + + async private void PopSelectPages() + { + int pageIndex = Navigation.NavigationStack.Count - 2; + + while (Navigation.NavigationStack[pageIndex].GetType().Equals(this.GetType())) + { + if (pageIndex <= 0) break; // do not remove root page + + Navigation.RemovePage(Navigation.NavigationStack[pageIndex]); + pageIndex--; + } + + await Navigation.PopAsync(); + } + + void OnScrolled(object sender, ScrolledEventArgs e) => Scroller(e.ScrollY); + + void Scroller(double y) + { + lastScrollPos = y; + focusedIndex = 0; + double focusedDistance = 100; + + for (int i = 0; i < activityStack.Children.Count; i++) + { + Label activityLabel = activityStack.Children[i] as Label; + + double distanceFromCenter = i == 0 ? y : Math.Abs(y - ((i * SCROLL_INTERVAL))); + + double scale = MAX_SCALE - (distanceFromCenter / 750); + + activityLabel.Scale = scale < MIN_SCALE ? MIN_SCALE + : scale > MAX_SCALE ? MAX_SCALE + : scale; + + if (distanceFromCenter < focusedDistance) + { + focusedDistance = distanceFromCenter; + focusedIndex = i; + } + activityLabel.TextColor = Color.White; + } + + isScrolling = true; + DetectEndOfScrolling(); + } + + /// + /// Determines that scrolling has ended (and stops polling) if isScrolling flag stays false at the end of timer, + /// and afterwards triggers focusing of current item to center of screen. + /// + private void DetectEndOfScrolling() + { + if (isTimerRunning) + return; + + isTimerRunning = true; + Device.StartTimer(TimeSpan.FromMilliseconds(100), () => { + if (!isScrolling) + { + ActivityLabel focusedLabel = activityStack.Children[focusedIndex] as ActivityLabel; + double finalPos = focusedIndex * SCROLL_INTERVAL; + + scroller.ScrollToAsync(0, finalPos, true); + + if (lastScrollPos == finalPos || focusedIndex == activityStack.Children.Count - 1) + { + focusedLabel.TextColor = Color.FromHex($"0CAEFF"); + } + + selectBtn.IsVisible = focusedLabel.LabelActivity.id != -1; + isTimerRunning = false; + return false; + } + else + { + isScrolling = false; + return true; + } + }); + } + } + + // Class to attach Activity Object to label. Workaround to identify Activity clicked by user. + class ActivityLabel : Label + { + public Activity LabelActivity { get; set; } + + public ActivityLabel(Activity activity) : base() + { + LabelActivity = activity; + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml new file mode 100755 index 0000000..a6cc686 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + Display Time + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml.cs new file mode 100755 index 0000000..f811c7f --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SetDurationPage.xaml.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xamarin.Forms; +using Xamarin.Forms.Xaml; +using Tizen.Wearable.CircularUI.Forms; +using MapMyRun.Tizen.Views.Templates; +using MapMyRun.Tizen.ViewModels; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class SetDurationPage : CirclePage + { + public SettingCell longDurationCell; + + public SettingCell midDurationCell; + + public SettingCell shortDurationCell; + + public ObservableCollection settingsList; + + private SettingsPageModel pageViewModel; + + public SetDurationPage() + { + InitializeComponent(); + pageViewModel = SettingsPageModel.GetInstance(); + BindingContext = pageViewModel; + settingsList = new ObservableCollection(); + durationMenu.ItemsSource = settingsList; + + CreateSettingCells(); + UpdateSettingsCells(); + PopulateSettingsList(); + } + + protected override void OnAppearing() + { + UpdateSettingsCells(); + } + + private void CreateSettingCells() + { + longDurationCell = new SettingCell("16 seconds", "", true); + longDurationCell.RadioValue = "16 seconds"; + + midDurationCell = new SettingCell("12 seconds", "", true); + midDurationCell.RadioValue = "12 seconds"; + + shortDurationCell = new SettingCell("6 seconds", "", true); + shortDurationCell.RadioValue = "6 seconds"; + } + + private void UpdateSettingsCells() + { + longDurationCell.RadioSelected = (pageViewModel.DisplayTime == 16); + midDurationCell.RadioSelected = (pageViewModel.DisplayTime == 12); + shortDurationCell.RadioSelected = (pageViewModel.DisplayTime == 6); + } + + private void PopulateSettingsList() + { + settingsList.Add(longDurationCell); + settingsList.Add(midDurationCell); + settingsList.Add(shortDurationCell); + } + + public void OnItemTapped(object sender, ItemTappedEventArgs args) + { + SettingCell clickedCell = (SettingCell) args.Item; + + Console.WriteLine($"OnItemTapped Item:{clickedCell.Name}"); + } + + public void OnDurationChanged(object sender, CheckedChangedEventArgs args) + { + try + { + Radio radioItem = sender as Radio; + + if (radioItem.Value == null) return; + + switch (radioItem.Value) + { + case "6 seconds": + pageViewModel.DisplayTime = 6; + break; + case "12 seconds": + pageViewModel.DisplayTime = 12; + break; + case "16 seconds": + default: + pageViewModel.DisplayTime = 16; + break; + + } + } + catch (NullReferenceException e) + { + Console.WriteLine($"Empty sender. Will not update Display Time."); + } + + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml new file mode 100755 index 0000000..339bd99 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + Settings + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml.cs new file mode 100755 index 0000000..ccc9ee3 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/SettingsPage.xaml.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xamarin.Forms; +using Xamarin.Forms.Xaml; +using Tizen.Wearable.CircularUI.Forms; +using MapMyRun.Tizen.Views.Templates; +using MapMyRun.Tizen.ViewModels; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class SettingsPage : CirclePage + { + public SettingCell displayTimeCell; + + public SettingCell gpsCell; + + public SettingCell versionCell; + + public ObservableCollection settingsList; + + private SettingsPageModel settingsPageModel; + + public SettingsPage() + { + Console.WriteLine($"Settings Page constructor"); + InitializeComponent(); + settingsList = new ObservableCollection(); + settingsPageModel = SettingsPageModel.GetInstance(); + BindingContext = settingsPageModel; + SettingsList.ItemsSource = settingsList; + + CreateSettingCells(); + UpdateSettingsCells(); + PopulateSettingsList(); + } + + protected override void OnAppearing() + { + UpdateSettingsCells(); + } + + private void CreateSettingCells() + { + displayTimeCell = new SettingCell("Display Time"); + gpsCell = new SettingCell("GPS", "", true); + versionCell = new SettingCell("v2.4.4"); + } + + private void UpdateSettingsCells() + { + displayTimeCell.SubText = $"{settingsPageModel.DisplayTime} seconds"; + gpsCell.RadioSelected = settingsPageModel.GPSStatus; + } + + private void PopulateSettingsList() + { + settingsList.Add(displayTimeCell); + settingsList.Add(gpsCell); + settingsList.Add(versionCell); + } + + async public void OnItemTapped(object sender, ItemTappedEventArgs args) + { + SettingCell clickedCell = (SettingCell) args.Item; + + Console.WriteLine($"OnItemTapped Item:{clickedCell.Name}"); + + if (clickedCell.Name.Equals("Display Time")) + { + await Navigation.PushAsync(new SetDurationPage()); + } + } + + public void OnGPSToggled(object sender, CheckedChangedEventArgs args) + { + SwitchCell switchCell = sender as SwitchCell; + try + { + if (switchCell.Text.Equals("GPS")) + { + settingsPageModel.GPSStatus = switchCell.On; + } + } catch (NullReferenceException e) + { + Console.WriteLine($"Empty sender. Will not update GPS status."); + } + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCell.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCell.cs new file mode 100755 index 0000000..0d5b539 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCell.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace MapMyRun.Tizen.Views.Templates +{ + public class SettingCell : INotifyPropertyChanged + { + public string Name { get; set; } + + private string _subText; + + public string SubText { + get + { + return _subText; + } + set + { + _subText = value; + OnPropertyChanged(); + } + } + + private bool _radioSelected; + + public bool RadioSelected + { + get + { + return _radioSelected; + } + set + { + _radioSelected = value; + OnPropertyChanged(); + } + } + + public string RadioValue { get; set; } + + public bool HasRadio { get; set; } + + public SettingCell(string name, string subText = "", bool addRadio = false) + { + Name = name; + SubText = subText; + HasRadio = addRadio; + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCellTemplateSelector.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCellTemplateSelector.cs new file mode 100755 index 0000000..afcd8fd --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/Templates/SettingCellTemplateSelector.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xamarin.Forms; + +namespace MapMyRun.Tizen.Views.Templates +{ + public class SettingCellTemplateSelector : DataTemplateSelector + { + public DataTemplate TextCellTemplate { get; set; } + public DataTemplate SwitchCellTemplate { get; set; } + + protected override DataTemplate OnSelectTemplate(object item, BindableObject container) + { + return ((SettingCell)item).HasRadio ? SwitchCellTemplate : TextCellTemplate; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml new file mode 100755 index 0000000..fa8cb70 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml.cs new file mode 100755 index 0000000..d98e8df --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutHistoryPage.xaml.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class WorkoutHistoryPage : CirclePage + { + // dummy data class + public class GroupModel : List> + { + public GroupModel() + { + Add(new NamedList("THIS WEEK") { new WorkoutSetupEntry() { Category="Intervals", Selected="11/27/2019 0:01 min" } }); + Add(new NamedList("OLDER") { new WorkoutSetupEntry() { Category="Run, on 11/13/2019", Selected="11/13/2019 0:04 min" } }); + } + } + + public class NamedList : List + { + public NamedList(string name) => Name = name; + public string Name { get; set; } + } + + public class WorkoutSetupEntry + { + public string Category + { + set; get; + } + + public string Selected + { + set; get; + } + } + + public WorkoutHistoryPage() + { + InitializeComponent(); + BindingContext = new GroupModel(); + + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml new file mode 100755 index 0000000..c3885e3 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml.cs new file mode 100755 index 0000000..b6f2d0a --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainIndexPage.xaml.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class WorkoutMainIndexPage : IndexPage + { + const int LEFT = 1; + const int RIGHT = -1; + + WorkoutMainPage _workoutIndexEntry1; + WorkoutMainPage _workoutIndexEntry2; + WorkoutMainPage _workoutIndexEntry3; + WorkoutMainPageGPS _workoutIndexEntry4; + + public WorkoutMainIndexPage() + { + InitializeComponent(); + _workoutIndexEntry1 = new WorkoutMainPage(0); + _workoutIndexEntry2 = new WorkoutMainPage(1); + _workoutIndexEntry3 = new WorkoutMainPage(2); + _workoutIndexEntry4 = new WorkoutMainPageGPS(); + + // workoutIndexEntry2.TranslationX = 400; + // workoutIndexEntry3.TranslationX = 400; + // workoutIndexEntry4.TranslationX = 400; + + _workoutIndexEntry1.RaiseRotaryEvent += RotateIndex; + _workoutIndexEntry2.RaiseRotaryEvent += RotateIndex; + _workoutIndexEntry3.RaiseRotaryEvent += RotateIndex; + _workoutIndexEntry4.RaiseRotaryEvent += RotateIndex; + + //_workoutIndexEntry1.RaiseBackButtonPressed += EnableSwitchPage; + //_workoutIndexEntry2.RaiseBackButtonPressed += EnableSwitchPage; + //_workoutIndexEntry3.RaiseBackButtonPressed += EnableSwitchPage; + //_workoutIndexEntry4.RaiseBackButtonPressed += EnableSwitchPage; + + _workoutIndexEntry1.RaiseBackButtonPressed += ToggleTimer; + _workoutIndexEntry2.RaiseBackButtonPressed += ToggleTimer; + _workoutIndexEntry3.RaiseBackButtonPressed += ToggleTimer; + _workoutIndexEntry4.RaiseBackButtonPressed += ToggleTimer; + + Children.Add(_workoutIndexEntry1); + Children.Add(_workoutIndexEntry2); + Children.Add(_workoutIndexEntry3); + Children.Add(_workoutIndexEntry4); + + CurrentPageChanged += BackgroundOpacityChange; + } + + public /*async*/ void RotateIndex(object sender, RotaryEventArgs args) + { + int direction = args.IsClockwise ? LEFT: RIGHT; + int index = Children.IndexOf(CurrentPage); + if (index + direction < 0 || index + direction > 3) return; + //CurrentPage.TranslateTo(400*-direction, CurrentPage.TranslationY, 300); + //Task.Delay(100); + CurrentPage = Children.ElementAt(index + direction); + //await Children.ElementAt(index + direction).TranslateTo(0, Children.ElementAt(index + direction).TranslationY, 200); + } + + //void EnableSwitchPage(bool buttonOn) + //{ + // if (buttonOn) this.InputTransparent = true; + // else this.InputTransparent = false; + //} + + void ToggleTimer(bool buttonOn) + { + _workoutIndexEntry1.TimerON(buttonOn); + } + + void BackgroundOpacityChange(object sender, EventArgs args) + { + int pageNumber = Children.IndexOf(CurrentPage) + 1; + foreach (var page in Children) + { + if(page is WorkoutMainPage p1) + { + if(pageNumber == 2) + { + p1.BackgroundBottomFade(on:true); + p1.BackgroundWholeFade(on:false); + } + else if(pageNumber == 3) + { + p1.BackgroundBottomFade(on:false); + p1.BackgroundWholeFade(on:true); + } + else if(pageNumber == 4) + { + p1.BackgroundBottomFade(on:false); + p1.BackgroundWholeFade(on:false); + } + } + else if (page is WorkoutMainPageGPS p2) + { + if (pageNumber == 2) + { + p2.BackgroundBottomFade(on: true); + p2.BackgroundWholeFade(on: false); + } + else if (pageNumber == 3) + { + p2.BackgroundBottomFade(on: false); + p2.BackgroundWholeFade(on: true); + } + else if (pageNumber == 4) + { + p2.BackgroundBottomFade(on: false); + p2.BackgroundWholeFade(on: false); + } + } + } + } + } + +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml new file mode 100755 index 0000000..24ed03f --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml.cs new file mode 100755 index 0000000..c1fd2ba --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainLoadingPage.xaml.cs @@ -0,0 +1,95 @@ +using MapMyRun.Models.Workout; +using MapMyRun.Models.Workout.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class WorkoutMainLoadingPage : CirclePage + { + const int COUNT_THREE_SECONDS = 3; + const int COUNT_UA_ICON_MOMENT = 0; + const double ZERO_PROGRESS_BAR = 0.0; + const double HALF_PROGRESS_BAR = 0.5; + const double FULL_PROGRESS_BAR = 1.0; + const double ZERO_OPACITY = 0.0; + const double FULL_OPACITY = 1.0; + const double PROGRESS_INCREASE_STEP = 0.05; + const double OPACITY_INCREASE_STEP = 0.1; + const int FONT_SIZE_INCREASE_STEP = 2; + const int INIT_FONTSIZE = 50; + + Image image = new Image() + { + Source = "UAIcon.png", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + Opacity = ZERO_OPACITY + }; + + public WorkoutMainLoadingPage() + { + InitializeComponent(); + + int counter = COUNT_THREE_SECONDS; + label.Opacity = ZERO_OPACITY; + label.FontSize = INIT_FONTSIZE; + label.Text = counter.ToString(); + + // animation moves every 50ms, counter decreases every second + // hence, 20 loops for each second + // 3 -> 2 -> 1 -> UnderArmor Icon -> UnderArmor Icon gets bigger and fades out + Device.StartTimer(TimeSpan.FromMilliseconds(50), () => + { + if(progress.Value == FULL_PROGRESS_BAR) + { + --counter; + if(counter != COUNT_UA_ICON_MOMENT) + { + label.Text = counter.ToString(); + label.FontSize = INIT_FONTSIZE; + label.Opacity = ZERO_OPACITY; + progress.Value = ZERO_PROGRESS_BAR; + } + else + { + this.Content = image; + UAIcon(); + return false; + } + } + if(progress.Value <= HALF_PROGRESS_BAR) + { + label.FontSize += FONT_SIZE_INCREASE_STEP; + label.Opacity += OPACITY_INCREASE_STEP; + } + progress.Value += PROGRESS_INCREASE_STEP; + return true; + }); + + } + + async void UAIcon() + { + image.FadeTo(FULL_OPACITY, 500); + await image.ScaleTo(1.5, 500); + await Task.Delay(500); + image.FadeTo(ZERO_OPACITY, 300); + await image.ScaleTo(10, 300); + Application.Current.MainPage = new WorkoutMainIndexPage(); + + // TODO : Move proper position + Workout workout = (Application.Current as App).workout; + workout.StartWorkout(); + return; + } + + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml new file mode 100755 index 0000000..bf51d72 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml.cs new file mode 100755 index 0000000..8557602 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPage.xaml.cs @@ -0,0 +1,297 @@ +using MapMyRun.Models.Workout; +using MapMyRun.Tizen.ViewModels.WorkoutMain; +using System; +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class WorkoutMainPage : CirclePage, IRotaryEventReceiver + { + const int MAINCELL_SIZE = 22; + const int SPACING = 5; + const int TOP_SPACING = 60; + const int HEART_SIZE = 20; + const double UNPRESSED_OPACITY = 1.0; + const double PRESSED_OPACITY = 0.5; + Color TRANSPARENT_BACKGROUND = new Color(0, 0, 0, 0); + const double BUTTON_WIDTH_COEFFICIENT = 0.8; + const double BUTTON_HEIGHT_COEFFICIENT = 0.27; + const double BUTTON_XPOSITION_COEFFICIENT = 0.1; + const double BUTTON_YPOSITION_COEFFICIENT = 0.65; + const int BACKGROUND_FADE_TIME = 300; + const double FULL_OPACITY = 1.0; + const double ZERO_OPACITY = 0.0; + const double SUBCELL_WIDTH_COEFFICIENT = 0.45; + const double RIGHTSUBCELL_XPOSITION_COEFFICIENT = 0.55; + const double BOTTOMCELL_WIDTH_COEFFICIENT = 0.2; + const double BOTTOMCELL_XPOSITION_COEFFICIENT = 0.4; + + bool buttonON; + + public TapGestureRecognizer recognizer = new TapGestureRecognizer(); + + public event EventHandler RaiseRotaryEvent; + public Action RaiseBackButtonPressed; + + // Controls + Label MainCell, SubCellLeft, SubCellRight, MainLabel, SubLabelLeft, SubLabelRight, BottomLabel; + Image BottomCell, BackgroundBottomLight, BackgroundWholeLight; + ImageButton FinishButton; + + WorkoutMainPageModel _workoutMainPageModel; + int _pageNumber; + + public WorkoutMainPage(int pageNumber) + { + InitializeComponent(); + + _pageNumber = pageNumber; + _workoutMainPageModel = new WorkoutMainPageModel((Application.Current as App).workout); + + // Controls + BackgroundBottomLight = new Image() + { + Source = "backgroundBottomLight.png", + Opacity = FULL_OPACITY + }; + + BackgroundWholeLight = new Image() + { + Source = "backgroundWholeLight.png", + Opacity = ZERO_OPACITY + }; + + MainCell = new Label() + { + HorizontalTextAlignment = TextAlignment.Center, + VerticalTextAlignment = TextAlignment.Center, + FontSize = MAINCELL_SIZE, + BackgroundColor = TRANSPARENT_BACKGROUND, + TextColor = Color.White + }; + MainCell.SetBinding(Label.TextProperty, nameof(IWorkoutItemViewModel.Value)); + + MainLabel = new Label() + { + HorizontalTextAlignment = TextAlignment.Center, + VerticalTextAlignment = TextAlignment.Center, + FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)), + BackgroundColor = TRANSPARENT_BACKGROUND, + TextColor = Color.White + }; + MainLabel.SetBinding(Label.TextProperty, nameof(IWorkoutItemViewModel.LongLabel)); + + SubCellLeft = new Label() + { + HorizontalTextAlignment = TextAlignment.End, + VerticalTextAlignment = TextAlignment.Center, + FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), + BackgroundColor = TRANSPARENT_BACKGROUND, + TextColor = Color.White + }; + SubCellLeft.SetBinding(Label.TextProperty, nameof(IWorkoutItemViewModel.Value)); + + SubLabelLeft = new Label() + { + HorizontalTextAlignment = TextAlignment.End, + VerticalTextAlignment = TextAlignment.Center, + FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)), + BackgroundColor = TRANSPARENT_BACKGROUND, + TextColor = Color.White + }; + SubLabelLeft.SetBinding(Label.TextProperty, nameof(IWorkoutItemViewModel.MediumLabel)); + + SubCellRight = new Label() + { + HorizontalTextAlignment = TextAlignment.Start, + VerticalTextAlignment = TextAlignment.Center, + FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), + BackgroundColor = TRANSPARENT_BACKGROUND, + TextColor = Color.White + }; + SubCellRight.SetBinding(Label.TextProperty, nameof(IWorkoutItemViewModel.Value)); + + SubLabelRight = new Label() + { + HorizontalTextAlignment = TextAlignment.Start, + VerticalTextAlignment = TextAlignment.Center, + FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)), + BackgroundColor = TRANSPARENT_BACKGROUND, + TextColor = Color.White + }; + SubLabelRight.SetBinding(Label.TextProperty, nameof(IWorkoutItemViewModel.MediumLabel)); + + BottomCell = new Image() + { + Source = "BPMIcon.png", + HeightRequest = HEART_SIZE, + WidthRequest = HEART_SIZE, + BackgroundColor = TRANSPARENT_BACKGROUND + }; + + BottomLabel = new Label() + { + HorizontalTextAlignment = TextAlignment.Center, + VerticalTextAlignment = TextAlignment.Center, + FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)), + BackgroundColor = TRANSPARENT_BACKGROUND, + TextColor = Color.White + }; + BottomLabel.SetBinding(Label.TextProperty, nameof(IWorkoutItemViewModel.ValueWithUnit)); + + FinishButton = new ImageButton() + { + Source = ImageSource.FromFile("finish-btn.png") + }; + FinishButton.Pressed += (s, e) => { + FinishButton.Opacity = PRESSED_OPACITY; + }; + FinishButton.Released += (s, e) => + { + FinishButton.Opacity = UNPRESSED_OPACITY; + }; + + buttonON = false; + MainCell.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 0); + MainLabel.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 0); + SubCellLeft.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 1); + SubLabelLeft.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 1); + SubCellRight.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 2); + SubLabelRight.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 2); + BottomCell.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 3); + BottomLabel.BindingContext = _workoutMainPageModel.GetWorkoutViewModel(pageNumber, 3); + + layout.Children.Add(BackgroundBottomLight, + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height;}), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; })); + + layout.Children.Add(BackgroundWholeLight, + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; })); + + layout.Children.Add(MainCell, + yConstraint: Constraint.RelativeToParent((parent) => { return parent.Y + TOP_SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; })); + + layout.Children.Add(MainLabel, + yConstraint: Constraint.RelativeToView(MainCell, (parent, sibling) => { return sibling.Y + sibling.Height + SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; })); + + layout.Children.Add(SubCellLeft, + yConstraint: Constraint.RelativeToView(MainLabel, (parent, sibling) => { return sibling.Y + sibling.Height + 2*SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * SUBCELL_WIDTH_COEFFICIENT; })); + + layout.Children.Add(SubLabelLeft, + yConstraint: Constraint.RelativeToView(SubCellLeft, (parent, sibling) => { return sibling.Y + sibling.Height + SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * SUBCELL_WIDTH_COEFFICIENT; })); + + layout.Children.Add(SubCellRight, + xConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * RIGHTSUBCELL_XPOSITION_COEFFICIENT; }), + yConstraint: Constraint.RelativeToView(MainLabel, (parent, sibling) => { return sibling.Y + sibling.Height + 2*SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * SUBCELL_WIDTH_COEFFICIENT; })); + + layout.Children.Add(SubLabelRight, + xConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * RIGHTSUBCELL_XPOSITION_COEFFICIENT; }), + yConstraint: Constraint.RelativeToView(SubCellRight, (parent, sibling) => { return sibling.Y + sibling.Height + SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * SUBCELL_WIDTH_COEFFICIENT; })); + + layout.Children.Add(BottomCell, + xConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * BOTTOMCELL_XPOSITION_COEFFICIENT; }), + yConstraint: Constraint.RelativeToView(SubLabelLeft, (parent, sibling) => { return sibling.Y + sibling.Height + 2*SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * BOTTOMCELL_WIDTH_COEFFICIENT; })); + + layout.Children.Add(BottomLabel, + yConstraint: Constraint.RelativeToView(BottomCell, (parent, sibling) => { return sibling.Y + sibling.Height + SPACING; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; })); + + recognizer.Tapped += OnPageClick; + this.Content.GestureRecognizers.Add(recognizer); + + FinishButton.Clicked += (s, e) => + { + Workout workout = (Application.Current as App).workout; + workout.FinishWorkout(); + Application.Current.MainPage = new NavigationPage(new MainPage()); + }; + } + + void OnPageClick(object s, EventArgs e) + { + var tempName = MainCell.BindingContext; + var tempVal = MainLabel.BindingContext; + + MainCell.BindingContext = SubCellLeft.BindingContext; + MainLabel.BindingContext = SubLabelLeft.BindingContext; + SubCellLeft.BindingContext = SubCellRight.BindingContext; + SubLabelLeft.BindingContext = SubLabelRight.BindingContext; + SubCellRight.BindingContext = tempName; + SubLabelRight.BindingContext = tempVal; + } + + public void TimerON(bool on) + { + // pageNumber 0, item 0 has DurationViewModel + IWorkoutItemViewModel itemViewModel = _workoutMainPageModel.GetWorkoutViewModel(0, 0); + if(itemViewModel is DurationViewModel _duration) + { + if (on) _duration.StopTimer(); + else _duration.ResumeTimer(); + } + } + + protected override bool OnBackButtonPressed() + { + if (buttonON == false) + { + MainCell.FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)); + Content.GestureRecognizers.Remove(recognizer); + BottomCell.IsVisible = false; + BottomLabel.IsVisible = false; + layout.Children.Add(FinishButton, + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * BUTTON_WIDTH_COEFFICIENT; }), + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height * BUTTON_HEIGHT_COEFFICIENT; }), + xConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * BUTTON_XPOSITION_COEFFICIENT; }), + yConstraint: Constraint.RelativeToParent((parent) => { return parent.Height * BUTTON_YPOSITION_COEFFICIENT; })); + buttonON = true; + RaiseBackButtonPressed?.Invoke(buttonON); + } + else + { + MainCell.FontSize = MAINCELL_SIZE; + Content.GestureRecognizers.Add(recognizer); + BottomCell.IsVisible = true; + BottomLabel.IsVisible = true; + + layout.Children.Remove(FinishButton); + buttonON = false; + RaiseBackButtonPressed?.Invoke(buttonON); + } + return true; + } + + public void Rotate(RotaryEventArgs args) + { + EventHandler< RotaryEventArgs> handler = RaiseRotaryEvent; + if(handler != null) + { + handler(this, args); + } + return; + } + + public void BackgroundBottomFade(bool on) + { + if (on == true) BackgroundBottomLight.FadeTo(1, BACKGROUND_FADE_TIME); + else BackgroundBottomLight.FadeTo(FULL_OPACITY, BACKGROUND_FADE_TIME); + } + + public void BackgroundWholeFade(bool on) + { + if (on == true) BackgroundWholeLight.FadeTo(1, BACKGROUND_FADE_TIME); + else BackgroundWholeLight.FadeTo(ZERO_OPACITY, BACKGROUND_FADE_TIME); + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml new file mode 100755 index 0000000..2913dd8 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml.cs new file mode 100755 index 0000000..aea6683 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutMainPageGPS.xaml.cs @@ -0,0 +1,165 @@ +using System; +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; +using MapMyRun.Tizen.ViewModels.WorkoutMain; +using MapMyRun.Models.Workout; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class WorkoutMainPageGPS : CirclePage, IRotaryEventReceiver + { + const double ZERO_OPACITY = 0.0; + const double FULL_OPACITY = 1.0; + const double OPACITY_DECREASE_STEP = -0.1; + const double OPACITY_INCREASE_STEP = 0.1; + const double UNPRESSED_OPACITY = 1.0; + const double PRESSED_OPACITY = 0.5; + const double BUTTON_WIDTH_COEFFICIENT = 0.8; + const double BUTTON_HEIGHT_COEFFICIENT = 0.27; + const double BUTTON_XPOSITION_COEFFICIENT = 0.1; + const double BUTTON_YPOSITION_COEFFICIENT = 0.65; + const int BACKGROUND_FADE_TIME = 300; + const double GPSIMAGE_WIDTH_COEFFICIENT = 0.1; + const double GPSIMAGE_HEIGHT_COEFFICIENT = 0.1; + const double GPSIMAGE_XPOSITION_COEFFICIENT = 0.45; + const double GPSIMAGE_YPOSITION_COEFFICIENT = 0.2; + const double TEXT_HEIGHT_COEFFICIENT = 0.8; + const double TEXT_YPOSITION_COEFFICIENT = 0.35; + + Label Textlabel; + Image GPSImage, BackgroundBottomLight, BackgroundWholeLight; + ImageButton FinishButton; + + bool buttonON; + double valueChanged; + + public event EventHandler RaiseRotaryEvent; + public Action RaiseBackButtonPressed; + public WorkoutMainPageGPS() + { + InitializeComponent(); + + buttonON = false; + string text = "Unable to\nacquire GPS signal.\n\nPlease move to\nopen space."; + string fixedTest = text.Replace("\n", Environment.NewLine); + + BackgroundBottomLight = new Image() + { + Source = "backgroundBottomLight.png", + Opacity = FULL_OPACITY + }; + + BackgroundWholeLight = new Image() + { + Source = "backgroundWholeLight.png", + Opacity = ZERO_OPACITY + }; + + GPSImage = new Image() + { + Source = "GPSIcon.png", + BackgroundColor = new Color(0, 0, 0, 0) + }; + + Textlabel = new Label() + { + Text = fixedTest, + TextColor = Color.White, + HorizontalTextAlignment = TextAlignment.Center + }; + + FinishButton = new ImageButton() + { + Source = ImageSource.FromFile("finish-btn.png") + }; + FinishButton.Pressed += (s, e) => { + FinishButton.Opacity = PRESSED_OPACITY; + }; + FinishButton.Released += (s, e) => + { + FinishButton.Opacity = UNPRESSED_OPACITY; + }; + + layout.Children.Add(BackgroundBottomLight, + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; })); + + layout.Children.Add(BackgroundWholeLight, + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; })); + + layout.Children.Add(GPSImage, + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height* GPSIMAGE_HEIGHT_COEFFICIENT; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width* GPSIMAGE_WIDTH_COEFFICIENT; }), + xConstraint: Constraint.RelativeToParent((parent) => { return parent.Width* GPSIMAGE_XPOSITION_COEFFICIENT; }), + yConstraint: Constraint.RelativeToParent((parent) => { return parent.Height* GPSIMAGE_YPOSITION_COEFFICIENT; })); + + layout.Children.Add(Textlabel, + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height* TEXT_HEIGHT_COEFFICIENT; }), + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width; }), + yConstraint: Constraint.RelativeToParent((parent) => { return parent.Height * TEXT_YPOSITION_COEFFICIENT; })); + + FinishButton.Clicked += (s, e) => + { + Workout workout = (Application.Current as App).workout; + workout.FinishWorkout(); + + Application.Current.MainPage = new NavigationPage(new MainPage()); + }; + + Device.StartTimer(TimeSpan.FromMilliseconds(100), () => + { + if (GPSImage.Opacity == ZERO_OPACITY) valueChanged = OPACITY_INCREASE_STEP; + else if (GPSImage.Opacity == FULL_OPACITY) valueChanged = OPACITY_DECREASE_STEP; + GPSImage.Opacity += valueChanged; + return true; + } + ); + } + + protected override bool OnBackButtonPressed() + { + if (buttonON == false) + { + layout.Children.Add(FinishButton, + widthConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * BUTTON_WIDTH_COEFFICIENT; }), + heightConstraint: Constraint.RelativeToParent((parent) => { return parent.Height* BUTTON_HEIGHT_COEFFICIENT; }), + xConstraint: Constraint.RelativeToParent((parent) => { return parent.Width * BUTTON_XPOSITION_COEFFICIENT; }), + yConstraint: Constraint.RelativeToParent((parent)=> { return parent.Height* BUTTON_YPOSITION_COEFFICIENT; })); + buttonON = true; + RaiseBackButtonPressed?.Invoke(buttonON); + } + else + { + layout.Children.Remove(FinishButton); + buttonON = false; + RaiseBackButtonPressed?.Invoke(buttonON); + } + return true; + } + + public void Rotate(RotaryEventArgs args) + { + EventHandler handler = RaiseRotaryEvent; + if (handler != null) + { + handler(this, args); + } + return; + } + + public void BackgroundBottomFade(bool on) + { + if (on == true) BackgroundBottomLight.FadeTo(FULL_OPACITY, BACKGROUND_FADE_TIME); + else BackgroundBottomLight.FadeTo(ZERO_OPACITY, BACKGROUND_FADE_TIME); + } + + public void BackgroundWholeFade(bool on) + { + if (on == true) BackgroundWholeLight.FadeTo(FULL_OPACITY, BACKGROUND_FADE_TIME); + else BackgroundWholeLight.FadeTo(ZERO_OPACITY, BACKGROUND_FADE_TIME); + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml new file mode 100755 index 0000000..8747683 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml.cs b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml.cs new file mode 100755 index 0000000..96f83c5 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.Tizen/views/WorkoutSetupPage.xaml.cs @@ -0,0 +1,64 @@ +using MapMyRun.Tizen.ViewModels; +using MapMyRun.Tizen.Views.Templates; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tizen.Wearable.CircularUI.Forms; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace MapMyRun.Tizen.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class WorkoutSetupPage : CirclePage + { + private WorkoutSetupPageModel setupPageModel; + + private ObservableCollection workoutSetup; + + private SettingCell currentActivity; + + public WorkoutSetupPage(WorkoutSetupPageModel _setupPageModel) + { + InitializeComponent(); + + setupPageModel = _setupPageModel; + BindingContext = setupPageModel; + + workoutSetup = new ObservableCollection(); + + currentActivity = new SettingCell("Select Activity", setupPageModel.currentActivity); + workoutSetup.Add(currentActivity); + workoutSetup.Add(new SettingCell("Goal", "Basic Workout")); + + workoutSetupList.ItemsSource = workoutSetup; + } + + protected override void OnAppearing() + { + base.OnAppearing(); + + currentActivity.SubText = setupPageModel.currentActivity; + } + + async private void workoutSetupList_ItemTapped(object sender, ItemTappedEventArgs e) + { + string tappedItem = workoutSetup[e.ItemIndex].Name; + + switch (tappedItem) + { + case "Select Activity": + await Navigation.PushAsync(new SelectWorkoutPage(setupPageModel)); + break; + case "Goal": + await DisplayAlert($"{tappedItem}", "Not Implemented", "OK"); + break; + default: + break; + } + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun.sln b/test/UnderArmour/MapMyRun/MapMyRun.sln new file mode 100755 index 0000000..297db49 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29609.76 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapMyRun.Tizen", "MapMyRun.Tizen\MapMyRun.Tizen.csproj", "{9645B7A1-AFE7-4150-BE16-278AB8C01A93}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapMyRun.Tests", "MapMyRun.Tests\MapMyRun.Tests.csproj", "{C23F236F-2FFE-437C-85C5-11329CE83834}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "MapMyRun", "MapMyRun\MapMyRun.shproj", "{3C82C042-F1F4-42E3-B82D-DFED5E06EE19}" +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + MapMyRun\MapMyRun.projitems*{3c82c042-f1f4-42e3-b82d-dfed5e06ee19}*SharedItemsImports = 13 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9645B7A1-AFE7-4150-BE16-278AB8C01A93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9645B7A1-AFE7-4150-BE16-278AB8C01A93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9645B7A1-AFE7-4150-BE16-278AB8C01A93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9645B7A1-AFE7-4150-BE16-278AB8C01A93}.Release|Any CPU.Build.0 = Release|Any CPU + {C23F236F-2FFE-437C-85C5-11329CE83834}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C23F236F-2FFE-437C-85C5-11329CE83834}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C23F236F-2FFE-437C-85C5-11329CE83834}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C23F236F-2FFE-437C-85C5-11329CE83834}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E7AF0B79-CC16-49A2-B49D-119F09B9D5A7} + EndGlobalSection +EndGlobal diff --git a/test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.projitems b/test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.projitems new file mode 100755 index 0000000..8622aba --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.projitems @@ -0,0 +1,51 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 3c82c042-f1f4-42e3-b82d-dfed5e06ee19 + + + MapMyRun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.shproj b/test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.shproj new file mode 100755 index 0000000..9bfde81 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/MapMyRun.shproj @@ -0,0 +1,13 @@ + + + + 3c82c042-f1f4-42e3-b82d-dfed5e06ee19 + 14.0 + + + + + + + + diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Activity.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Activity.cs new file mode 100755 index 0000000..01b0f19 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Activity.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public class Activity + { + public int id { get; set; } = -1; + + public int group_id { get; set; } = -1; + + public bool useGPS { get; set; } = false; + + public bool useDistance { get; set; } = false; + + public bool usePace { get; set; } = false; + + public bool shoesSupported { get; set; } = false; + + public bool cadenceSupported { get; set; } = false; + + public bool hasSteps { get; set; } = false; + + // 0 - unsupported, 1 - Cadence, 2 - Stride, while yet unable to handle enum + public int coachingSupported { get; set; } = 0; + + public string name { get; set; } = "undefined"; + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/ActivityRepository.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/ActivityRepository.cs new file mode 100755 index 0000000..0fcb080 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/ActivityRepository.cs @@ -0,0 +1,267 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace MapMyRun.Models +{ + public class ActivityRepository + { + // Activities data taken from mmr.activities.ts and activity.ts + private const string activityListJson = @"[ + { id: 1, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 7, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 9, group_id: 9, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 10, group_id: 10, useGPS: true, useDistance: true, usePace: false }, + { id: 16, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 19, group_id: 1, useGPS: false, useDistance: false, usePace: false }, + { id: 20, group_id: 8, useGPS: false, useDistance: true, usePace: false }, + { id: 21, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 22, group_id: 5, useGPS: false, useDistance: false, usePace: false }, + { id: 23, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 24, group_id: 9, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 26, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 28, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 29, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 31, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 33, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 34, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 36, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 41, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 43, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 44, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 45, group_id: 6, useGPS: true, useDistance: true, usePace: false, shoesSupported: true, cadenceSupported: true }, + { id: 45, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 45, group_id: 8, useGPS: true, useDistance: true, usePace: false }, + { id: 47, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 54, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 56, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 57, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 59, group_id: 8, useGPS: false, useDistance: false, usePace: false }, + { id: 60, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 65, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 68, group_id: 7, useGPS: true, useDistance: false, usePace: false }, + { id: 71, group_id: 6, useGPS: false, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 72, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 73, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 74, group_id: 10, useGPS: true, useDistance: true, usePace: false }, + { id: 75, group_id: 8, useGPS: false, useDistance: false, usePace: false }, + { id: 77, group_id: 5, useGPS: false, useDistance: false, usePace: false }, + { id: 80, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 82, group_id: 2, useGPS: false, useDistance: false, usePace: false }, + { id: 84, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 85, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 86, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 94, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 95, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 96, group_id: 5, useGPS: false, useDistance: false, usePace: false }, + { id: 99, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 101, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 102, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 103, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 104, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 105, group_id: 9, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 107, group_id: 10, useGPS: true, useDistance: true, usePace: false }, + { id: 108, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 111, group_id: 6, useGPS: false, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 115, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 116, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 119, group_id: 10, useGPS: true, useDistance: true, usePace: true }, + { id: 120, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 121, group_id: 8, useGPS: false, useDistance: false, usePace: false }, + { id: 123, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 124, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 127, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 128, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 131, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 133, group_id: 9, useGPS: true, useDistance: true, usePace: false, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 134, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 135, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 137, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 138, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 141, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 142, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 148, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 153, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 154, group_id: 7, useGPS: true, useDistance: true, usePace: true }, + { id: 155, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 156, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 161, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 163, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 164, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 169, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 171, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 172, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 176, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 178, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 180, group_id: 8, useGPS: true, useDistance: true, usePace: false }, + { id: 182, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 184, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 192, group_id: 8, useGPS: false, useDistance: false, usePace: false }, + { id: 193, group_id: 8, useGPS: false, useDistance: false, usePace: false }, + { id: 197, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 199, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 200, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 201, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 204, group_id: 9, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 205, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 208, group_id: 3, useGPS: false, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 208, group_id: 6, useGPS: false, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 214, group_id: 5, useGPS: false, useDistance: false, usePace: false }, + { id: 216, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 221, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 222, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 224, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 227, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 228, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 230, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 235, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 250, group_id: 6, useGPS: false, useDistance: false, usePace: false, shoesSupported: true, cadenceSupported: true }, + { id: 251, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 254, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 257, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 258, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 259, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 263, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 264, group_id: 7, useGPS: true, useDistance: true, usePace: true }, + { id: 266, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 267, group_id: 5, useGPS: false, useDistance: false, usePace: false }, + { id: 271, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 275, group_id: 9, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 284, group_id: 8, useGPS: false, useDistance: false, usePace: false }, + { id: 336, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 468, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 469, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 471, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 472, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 473, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 498, group_id: 11, useGPS: false, useDistance: false, usePace: false }, + { id: 499, group_id: 11, useGPS: false, useDistance: false, usePace: false }, + { id: 500, group_id: 11, useGPS: false, useDistance: false, usePace: false }, + { id: 546, group_id: 1, useGPS: false, useDistance: false, usePace: false }, + { id: 548, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 564, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 598, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 622, group_id: 2, useGPS: false, useDistance: false, usePace: false }, + { id: 627, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 633, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 637, group_id: 1, useGPS: true, useDistance: true, usePace: false }, + { id: 704, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 708, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 710, group_id: 6, useGPS: true, useDistance: true, usePace: false, shoesSupported: true, cadenceSupported: true }, + { id: 714, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 722, group_id: 11, useGPS: false, useDistance: false, usePace: false }, + { id: 724, group_id: 11, useGPS: false, useDistance: false, usePace: false }, + { id: 726, group_id: 11, useGPS: false, useDistance: false, usePace: false }, + { id: 728, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 730, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 732, group_id: 5, useGPS: false, useDistance: false, usePace: false }, + { id: 740, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 742, group_id: 2, useGPS: false, useDistance: false, usePace: false }, + { id: 746, group_id: 2, useGPS: false, useDistance: false, usePace: false }, + { id: 748, group_id: 2, useGPS: false, useDistance: false, usePace: false }, + { id: 750, group_id: 2, useGPS: false, useDistance: false, usePace: false }, + { id: 752, group_id: 2, useGPS: false, useDistance: false, usePace: false }, + { id: 754, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 756, group_id: 6, useGPS: false, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, coachingSupported: 1 }, + { id: 758, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 760, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 762, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 764, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 768, group_id: 7, useGPS: true, useDistance: true, usePace: false }, + { id: 813, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 819, group_id: 7, useGPS: false, useDistance: false, usePace: false }, + { id: 825, group_id: 9, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 827, group_id: 9, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true, hasSteps: true }, + { id: 829, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 831, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 845, group_id: 5, useGPS: false, useDistance: false, usePace: false }, + { id: 855, group_id: 6, useGPS: true, useDistance: true, usePace: true, shoesSupported: true, cadenceSupported: true }, + { id: 861, group_id: 3, useGPS: false, useDistance: false, usePace: false }, + { id: 863, group_id: 5, useGPS: true, useDistance: true, usePace: false }, + { id: 880, group_id: 4, useGPS: false, useDistance: false, usePace: false }, + { id: 882, group_id: 0, useGPS: false, useDistance: false, usePace: false }, + { id: 890, group_id: 3, useGPS: false, useDistance: false, usePace: false } + ]"; + + public List activityGroupings { get; private set; } + + public List completeActivityList { get; private set; } + + public List popularActivityList { get; private set; } + + private List activityListByGroup { get; set; } + + public List GetActivitiesByGroupId(int group_id) + { + activityListByGroup = new List(); + activityListByGroup = completeActivityList.FindAll((Activity activity) => { return activity.group_id == group_id; }); + + return activityListByGroup; + } + + private void GeneratePopularActivityList() + { + popularActivityList = new List(); + + List popularActivityIds = new List { 16, 266, 208, 829, 197, 9, 204, 24, 36, 41 }; + popularActivityIds.ForEach((int id) => { popularActivityList.Add(GetActivityById(id)); }); + } + + public ActivityRepository(string localizationJson) + { + completeActivityList = JsonConvert.DeserializeObject>(activityListJson); + UpdateNames(localizationJson); + DefineGroups(); + GeneratePopularActivityList(); + } + + public Activity GetActivityById(int id) + { + try + { + return completeActivityList.First((Activity activity) => activity.id == id); + } + catch (Exception e) + { + Console.WriteLine($"Exception at finding activity with id {id}. Check if activity exists."); + Console.WriteLine(e.StackTrace); + + return null; + } + } + + private void DefineGroups() + { + activityGroupings = new List(); + + activityGroupings.Add(new Activity() { group_id = 0, name = "Class" }); + activityGroupings.Add(new Activity() { group_id = 1, name = "Cycling" }); + activityGroupings.Add(new Activity() { group_id = 2, name = "Dance" }); + activityGroupings.Add(new Activity() { group_id = 3, name = "Gym" }); + activityGroupings.Add(new Activity() { group_id = 4, name = "Martial Arts" }); + activityGroupings.Add(new Activity() { group_id = 6, name = "Run" }); + activityGroupings.Add(new Activity() { group_id = 7, name = "Sport" }); + activityGroupings.Add(new Activity() { group_id = 8, name = "Swim" }); + activityGroupings.Add(new Activity() { group_id = 9, name = "Walk" }); + activityGroupings.Add(new Activity() { group_id = 10, name = "Winter" }); + activityGroupings.Add(new Activity() { group_id = 11, name = "Yoga" }); + activityGroupings.Add(new Activity() { group_id = 5, name = "Other" }); + } + + // NOTE: Implementation regarding retrieval of names to be updated when proper localization implementation is done + private void UpdateNames(string localizationJson) + { + JObject localizedStrings = JObject.Parse(localizationJson); + + const string activityKey = "activity_id"; + + completeActivityList.ForEach((Activity activity) => { + activity.name = localizedStrings[$"{activityKey}_{activity.id}"]?.ToString(); + }); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Dispatcher.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Dispatcher.cs new file mode 100755 index 0000000..c7deeb8 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Dispatcher.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRun.Models +{ + public class Dispatcher : IDispatcher + { + IMessagePortHandler _messagePortHandler = null; + private const string ServiceAppId = "org.tizen.example.MapMyRunService"; + //private const string ServiceAppId = "org.tizen.mapmyrunservice"; + private const string LocalPort = "APP_PORT"; + private const string RemotePort = "SERVICE_PORT"; + public const string OperationKey = "operation"; + public const string DataKey = "data"; + public const string TransIdKey = "transID"; + public const string ResponseStatusKey = "status"; + private const bool SecureMode = false; + private uint TransIdIndex = 0; + private readonly Dictionary RequestCache = new Dictionary(); + private readonly List NotificationProviders = new List(); + + public static Dispatcher Instance { get; private set; } = new Dispatcher(); + + private Dispatcher() + { + } + + public void Initialize(IMessagePortHandler messagePortHandler) + { + _messagePortHandler = messagePortHandler; + _messagePortHandler.MessageReceived += OnReceiveMessage; + _messagePortHandler.Connect(LocalPort, ServiceAppId, RemotePort, SecureMode); + } + + public string SendRequest(Request request) + { + request.TransID = TransIdIndex.ToString(); + TransIdIndex++; + RequestCache.Add(request.TransID, request); + var dataSet = new Dictionary + { + { OperationKey, ((int)request.Operation).ToString() }, + { DataKey, request.Data }, + { TransIdKey, request.TransID } + }; + + _messagePortHandler.Send(dataSet); + return request.TransID; + } + + public void OnReceiveMessage(Dictionary dataSet) + { + if (dataSet.TryGetValue(OperationKey, out string operation)) + { + Console.WriteLine($"Receive Notification: {operation}"); + + try + { + OperationType type = (OperationType)Int32.Parse(operation); + if (dataSet.TryGetValue(DataKey, out string data)) + PostNotification(data, type); + } + catch + { + Console.WriteLine("Invalid Operation"); + } + return; + } + + if (!dataSet.TryGetValue(TransIdKey, out string tid)) + { + Console.WriteLine("Request for operation response is not found"); + return; + } + + if (RequestCache.TryGetValue(tid, out Request cachedRequest)) + { + dataSet.TryGetValue(DataKey, out string data); + dataSet.TryGetValue(ResponseStatusKey, out string status); + + if (!Enum.TryParse(status, out ResponseStatus responseStatus)) + { + responseStatus = ResponseStatus.Fail; + } + + var response = new Response(tid, data, responseStatus); + cachedRequest.OnReceiveResponse(response); + } + } + + void PostNotification(string data, OperationType type) + { + NotificationProvider notificationMonitor; + try + { + notificationMonitor = NotificationProviders.Find(x => x != null && x.Type == type); + notificationMonitor.PostNotification(data); + } + catch + { + Console.WriteLine($"Notification Monitor none: {type}"); + } + } + + public NotificationProvider Listen(OperationType type) + { + NotificationProvider notificationMonitor; + try + { + notificationMonitor = NotificationProviders.Find(x => x != null && x.Type == type); + Console.WriteLine($"Listen exist Notification Monitor {notificationMonitor.Type}"); + return notificationMonitor; + } + catch + { + notificationMonitor = new NotificationProvider(type); + NotificationProviders.Add(notificationMonitor); + Console.WriteLine($"Create new Notification Monitor {notificationMonitor.Type}"); + return notificationMonitor; + } + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/IAppLauncher.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/IAppLauncher.cs new file mode 100755 index 0000000..c5096cb --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/IAppLauncher.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public interface IAppLauncher + { + void Launch(string appId); + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/IDispatcher.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/IDispatcher.cs new file mode 100755 index 0000000..a82bb26 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/IDispatcher.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRun.Models +{ + public interface IDispatcher + { + void Initialize(IMessagePortHandler messagePortHandler); + + string SendRequest(Request request); + + void OnReceiveMessage(Dictionary data); + + NotificationProvider Listen(OperationType type); + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/IMessagePortHandler.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/IMessagePortHandler.cs new file mode 100755 index 0000000..3d02121 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/IMessagePortHandler.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRun.Models +{ + public interface IMessagePortHandler + { + void Connect(string localPort, string remoteAppId, string remotePort, bool secureMode); + + void Disconnect(); + + void Send(Dictionary data); + + event Action> MessageReceived; + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/IPermissionManager.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/IPermissionManager.cs new file mode 100755 index 0000000..13d806a --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/IPermissionManager.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public enum PermissionType + { + Location, + Sensor + } + + public delegate void OnPermissionResponse(bool isSuccess, bool isGranted); + + public interface IPermissionManager + { + bool HasPermission(PermissionType permission); + + void RequestPermission(PermissionType permission, OnPermissionResponse onResponse); + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/IPhoneService.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/IPhoneService.cs new file mode 100755 index 0000000..aa9ba73 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/IPhoneService.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public interface IPhoneService + { + bool IsPlatformSwVersionSupported(); + + bool IsAuthenticated(Action authenticatedCb); + + void InitializeApp(); + + void KeepScreenOn(); + + void SetAppTerminateTimer(int sec); + + void ClearAppTerminateTimer(); + + void OnAppStart(); + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/IPlatformService.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/IPlatformService.cs new file mode 100755 index 0000000..817d8a2 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/IPlatformService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public interface IPlatformService + { + void GetSwVersion(out string version); + + void KeepScreenOn(); + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Loading.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Loading.cs new file mode 100755 index 0000000..df6afec --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Loading.cs @@ -0,0 +1,316 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MapMyRun.Models.Settings; + +namespace MapMyRun.Models +{ + public enum LoadingState + { + Not_Started = 1, + Connect= 2, + Install = 3, + Auth = 4, + Initializers = 5, + Finished = 6, + } + + public class Loading + { + LoadingState _state = LoadingState.Not_Started; + readonly string _serviceAppId ; + readonly IAppLauncher _appLauncher; + readonly IDispatcher _dispatcher; + readonly IPhoneService _phoneService; + readonly IMessagePortHandler _messagePortHandler; + readonly ISettingManager _settingManager; + NotificationObserver _logoutObserver; + public LoadingState State + { + get { return _state; } + set + { + _state = value; + LoadingStateChanged?.Invoke(_state); + } + } + + public event Action LoadingStateChanged; + public PhoneApplicationState phoneState = PhoneApplicationState.NotSupportedTizenVersion; + + public Loading(IAppLauncher appLauncher, IMessagePortHandler messagePortHandler, + IDispatcher dispatcher, IPhoneService phoneService, ISettingManager settingManager, string serviceAppId) + { + _appLauncher = appLauncher; + _serviceAppId = serviceAppId; + _dispatcher = dispatcher; + _messagePortHandler = messagePortHandler; + _phoneService = phoneService; + _settingManager = settingManager; + } + + public void StartLoading() + { + if (!_phoneService.IsPlatformSwVersionSupported()) + return; + + _phoneService.KeepScreenOn(); + + State = LoadingState.Connect; + if (!LaunchServiceApp()) + return; + + ConnectServiceApp(); + + //TODO : Move to converter + GetUserProfileForDisplayMeasurementSystem(); + + State = LoadingState.Auth; + + _phoneService.IsAuthenticated((state) => + { + _phoneService.InitializeApp(); + UpdatePhoneState(state); + }); + } + + private void UpdatePhoneState(PhoneApplicationState state) + { + phoneState = state; + switch (state) + { + case PhoneApplicationState.NoInternetConnection: + case PhoneApplicationState.NoBTE: + case PhoneApplicationState.Timeout: + case PhoneApplicationState.NotInstalled: + case PhoneApplicationState.NotLoggedIn: + case PhoneApplicationState.NotSupportedTizenVersion: + //this._isLoadingError = true; + break; + case PhoneApplicationState.LoggedIn: + case PhoneApplicationState.Authenticated: + StartInitialization(); + break; + } + } + + private void StartInitialization() + { + State = LoadingState.Initializers; + + InitializeLogoutEventHandler(); + + InitializeUserData(); + + InitializeConsent(); + + InitializeUserGear(); + + InitializeSettings(); + + State = LoadingState.Finished; + + InitializeAfterLoading(); + } + + private void GetUserProfileForDisplayMeasurementSystem() + { + var request = new Request(OperationType.GetData, "{ \"action\":" + (int)DataActionType.GetUserProfile + " }"); + + request.ResponseReceived += (Response response) => + { + //TODO : Measurement Converter + Console.WriteLine($"Get User Profile for Measurement System"); + }; + _dispatcher.SendRequest(request); + } + + private void SendConsentOkReturn() + { + var request = new Request(OperationType.GetConsentStatus, "{ \"isUpToDate\": true }"); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get Consent Status"); + }; + _dispatcher.SendRequest(request); + } + + private void SendGearStatusOkReturn() + { + var request = new Request(OperationType.GetUserGear, "{ \"isUpToDate\": true }"); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get Consent Status"); + }; + _dispatcher.SendRequest(request); + } + + public void SendTestMessage() + { + var startRequest = new Request(OperationType.ApplicationInitialized, ""); + + startRequest.ResponseReceived += (Response response) => + { + // TODO : Implement Cache Storage + //if (result && result.data) + //{ + // this.storageService.save(DEVICE_TYPE_STORAGE_KEY, result.data.deviceType); + //} + Console.WriteLine($"PhoneService: Initialize app request result"); + }; + _dispatcher.SendRequest(startRequest); + } + + private bool LaunchServiceApp() + { + Console.WriteLine($"Launching service app! {_serviceAppId}"); + try + { + _appLauncher.Launch(_serviceAppId); + } + catch (Exception e) + { + //TODO : Handle Fail Case + Console.WriteLine($"Unable to launch app {_serviceAppId }"); + Console.WriteLine(e.StackTrace); + return false; + } + + Console.WriteLine($"Launching service app SUCCESS!"); + return true; + } + + private void ConnectServiceApp() + { + Console.WriteLine($"Connect to service app"); + try + { + _dispatcher.Initialize(_messagePortHandler); + } + catch (Exception e) + { + //TODO : Handle Fail Case + Console.WriteLine(e.StackTrace); + } + State = LoadingState.Install; + } + + public void InitializeLogoutEventHandler() + { + _logoutObserver = new NotificationObserver(OperationType.Logout); + _logoutObserver.NotificationReceived += (string data) => + { + //TODO : Logout procedure + Console.WriteLine("Logout!"); + }; + + var monitor = _dispatcher.Listen(OperationType.Logout); + _logoutObserver.Subscribe(monitor); + } + + public void InitializeUserData() + { + var request = new Request(OperationType.GetData, "{ \"action\":" + (int)DataActionType.GetUpdatedUserProfile + " }"); + + request.ResponseReceived += (Response response) => + { + //TODO : User Data Service + Console.WriteLine($"Get Updated User Profile"); + }; + _dispatcher.SendRequest(request); + } + + public void InitializeConsent() + { + var request = new Request(OperationType.GetConsentStatus, "{ \"isUpToDate\": false }"); + + request.ResponseReceived += (Response response) => + { + //TODO : Consent Fail Case + Console.WriteLine($"Get Consent Status"); + + SendConsentOkReturn(); + }; + _dispatcher.SendRequest(request); + } + + public void InitializeUserGear() + { + var request = new Request(OperationType.GetUserGear, "{ \"isUpToDate\": false }"); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get User Gear Status"); + SendGearStatusOkReturn(); + }; + _dispatcher.SendRequest(request); + } + + public void InitializeSettings() + { + _settingManager.Initialize(() => + { + GetWorkoutSettings(); + }); + } + + private void GetWorkoutSettings() + { + var request = new Request(OperationType.GetWorkoutSettings, ""); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get Workout Settings"); + }; + _dispatcher.SendRequest(request); + } + + public void InitializeAfterLoading() + { + _phoneService.OnAppStart(); + + InitializeSContext(); + + InitializeGpsAcquire(); + + InitializeTrainingPlan(); + } + + void InitializeSContext() + { + var request = new Request(OperationType.CheckSContext, ""); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get SContext Status"); + }; + _dispatcher.SendRequest(request); + } + + void InitializeGpsAcquire() + { + var request = new Request(OperationType.StartGpsAcquire, ""); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Start Gps Acquire"); + }; + _dispatcher.SendRequest(request); + } + + void InitializeTrainingPlan() + { + var request = new Request(OperationType.GetTodayTrainingPlans, ""); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get Training Plans"); + }; + _dispatcher.SendRequest(request); + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationObserver.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationObserver.cs new file mode 100755 index 0000000..d5b53a4 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationObserver.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public class NotificationObserver : IObserver + { + public OperationType Type { get; } + public event Action NotificationReceived; + public event Action NotificationErrorReceived; + IDisposable _cancellation; + + public NotificationObserver(OperationType type) + { + Type = type; + } + + public void Subscribe(NotificationProvider provider) + { + _cancellation = provider.Subscribe(this); + } + + public void Unsubscribe() + { + _cancellation.Dispose(); + } + + public void OnCompleted() + { + Console.WriteLine($"Notification Observer Completed Type: {Type}"); + } + + public void OnError(Exception error) + { + NotificationErrorReceived(error); + } + + public void OnNext(string value) + { + NotificationReceived(value); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationProvider.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationProvider.cs new file mode 100755 index 0000000..e3526e9 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/NotificationProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public class NotificationProvider : IObservable + { + readonly List> observers; + public OperationType Type { get; } + public string Name { get; } + + public NotificationProvider(OperationType type) + { + Type = type; + observers = new List>(); + } + + public void PostNotification(string data) + { + foreach (var observer in observers) + observer.OnNext(data); + } + + public IDisposable Subscribe(IObserver observer) + { + if (!observers.Contains(observer)) + observers.Add(observer); + return new Unsubscriber(observers, observer); + } + + private class Unsubscriber : IDisposable + { + private readonly List> _observers; + private readonly IObserver _observer; + + public Unsubscriber(List> observers, IObserver observer) + { + _observers = observers; + _observer = observer; + } + + public void Dispose() + { + if (_observer != null && _observers.Contains(_observer)) + _observers.Remove(_observer); + } + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/PermissionManager.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/PermissionManager.cs new file mode 100755 index 0000000..f46adcc --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/PermissionManager.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public class PermissionManager : IPermissionManager + { + const string LocationPrivilege = "http://tizen.org/privilege/location"; + const string SensorPrivilege = "http://tizen.org/privilege/healthinfo"; + + bool _isLocationPermitted = false; + bool _isSensorPermitted = false; + IDispatcher _dispatcher; + + public PermissionManager(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + } + + public bool HasPermission(PermissionType permission) + { + if (permission == PermissionType.Location) + return _isLocationPermitted; + else if (permission == PermissionType.Sensor) + return _isSensorPermitted; + else + return false; + } + + void StoreCache(PermissionType permission, bool isPermitted) + { + if (permission == PermissionType.Location) + _isLocationPermitted = isPermitted; + else if (permission == PermissionType.Sensor) + _isSensorPermitted = isPermitted; + } + + public void RequestPermission(PermissionType permission, OnPermissionResponse onResponse) + { + var privilege = ""; + if (permission == PermissionType.Location) + { + privilege = LocationPrivilege; + } + else if (permission == PermissionType.Sensor) + { + privilege = SensorPrivilege; + } + else + { + onResponse(false, false); + return; + } + + var request = new Request(OperationType.RequestPermission, privilege); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get Request Permission Response {response.Data}"); + if (response.Status != ResponseStatus.Success) + { + onResponse(false, false); + return; + } + + if (response.Data.Equals("true")) + { + StoreCache(permission, true); + onResponse(true, true); + } + else + { + StoreCache(permission, false); + onResponse(true, false); + } + }; + _dispatcher.SendRequest(request); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/PhoneService.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/PhoneService.cs new file mode 100755 index 0000000..6773b52 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/PhoneService.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models +{ + public enum PhoneApplicationState + { + NotSupportedTizenVersion, + LoggedIn, + Authenticated, + NoBTE, + NoInternetConnection, + Timeout, + NotInstalled, + NotLoggedIn, + NotAuthenticated, + NoConsentAgreement, + WaitingForConsentAcceptance + } + + public class PhoneService : IPhoneService + { + private const string SwVersionPrefix = "R360"; + private const string SwVersionSuffix = "BPK5"; + readonly IPlatformService _platformService; + readonly IDispatcher _dispatcher; + public PhoneService(IDispatcher dispatcher, IPlatformService platformService) + { + _platformService = platformService; + _dispatcher = dispatcher; + } + + public bool IsPlatformSwVersionSupported() + { + _platformService.GetSwVersion(out string version); + + if (version.StartsWith(SwVersionPrefix)) + { + if (string.Compare(version.Substring(version.Length - 4), SwVersionSuffix) < 0) + return false; + } + + return true; + } + + public bool IsAuthenticated(Action authenticatedCb) + { + //TODO : Check cache + //TODO : Set Timer 120 sec + + SendIsAuthenticatedRequest(authenticatedCb); + return true; + } + + public void InitializeApp() + { + var request = new Request(OperationType.ApplicationInitialized, ""); + + request.ResponseReceived += (Response response) => + { + // TODO : Implement Cache Storage to save watch type + //if (result && result.data) + //{ + // this.storageService.save(DEVICE_TYPE_STORAGE_KEY, result.data.deviceType); + //} + Console.WriteLine($"Send UI App Initialization signal"); + }; + _dispatcher.SendRequest(request); + } + + public void KeepScreenOn() + { + _platformService.KeepScreenOn(); + } + + public void SetAppTerminateTimer(int sec) + { + } + + public void ClearAppTerminateTimer() + { + } + + private void SendIsAuthenticatedRequest(Action authenticatedCb) + { + var request = new Request(OperationType.CheckIsAuthenticated, "{ \"authenticate\": false }"); + + request.ResponseReceived += (Response response) => + { + //TODO : Cache to keep auth status + //TODO : Check response data and Fail case + Console.WriteLine($"Get authenticate status"); + + authenticatedCb(PhoneApplicationState.LoggedIn); + }; + _dispatcher.SendRequest(request); + } + + public void OnAppStart() + { + var request = new Request(OperationType.ApplicationStarted, ""); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine("App Start!"); + }; + _dispatcher.SendRequest(request); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Request.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Request.cs new file mode 100755 index 0000000..aed16bd --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Request.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRun.Models +{ + public enum OperationType + { + PrepareWKO = 1, + StartWKO, + PauseWKO, + ResumeWKO, + FinishWKO, + SaveWKO, + DiscardWKO, + CheckSContext, + CheckIsLoggedIn, + NavigateToLogin, + NavigateToProfile, + CheckIsAuthenticated, + CheckIsInstalled, + GetData, + UpdateNutritionPreference, + GetSleepScore, + ExitApp, + MFPGetCurrentSteps, + MFPAddEnergy, + MFPAddWater, + GetSettings, + SetSettings, + GetWorkoutSettings, + SetWorkoutSettings, + UnfinishedWorkoutRestore, + RestoreWKO, + StartGpsAcquire, + StopGpsAcquire, + CheckNetworkConnection, + ChangeNutritionSource, + GetConsentStatus, + GetUserGear, + ConnectUserGear, + DisconnectUserGear, + RequestPermission, + GetTodayTrainingPlans, + // More operations to be here + OperationMax = GetTodayTrainingPlans, + // Notification Type Start + WkoDataChanged, + HRData, + AggregatesData, + HRZone, + IntensityChanged, + GetDiarySummary, + GetCachedDiarySummary, + StepsChanged, + SapInitialized, + ApplicationInitialized, + ApplicationStarted, + NoBluetoothConnection, + WristUpEvent, + MfpDataUpdated, + SapRemoteServiceNotFound, + WillpowerChanged, + EnergyChanged, + SpeedChanged, + GpsStateChanged, + ShoesStateChanged, + MeasurementPreferenceChanged, + NutritionPreferencesUpdated, + SleepScoreReceived, + WorkoutSyncSuccess, + WorkoutSyncFailed, + UserProfileUpdated, + LocationChanged, + Logout, + SettingsChanged, + WorkoutSettingsChanged, + SapConnectionTimeout, + LangChanged, + HapticFeedback, + NutritionSourceSupportChanged, + CadenceChanged, + StrideLengthChanged, + RTFCUpdate, + RTFCNotification, + RTSCUpdate, + RTSCNotification, + GoalUpdate, + GoalCompleted, + AppStateForeground, + AppStateBackground, + // More operations to be here + NotificationTypeMax = AppStateBackground + } + + public enum DataActionType + { + None = 0, + GetUserProfile, + GetUpdatedUserProfile, + PostWorkout, + GetUserGoals, + GetUpToDateUserGoals, + GetHrZones, + GetChallenges, + GetChallengeDetails, + GetWorkouts + } + + public class Request + { + public OperationType Operation { get; } + public string Data { get; } + public event Action ResponseReceived; + public string TransID { get; set; } + + public Request (OperationType operation, string data) + { + Operation = operation; + Data = data; + } + + public void OnReceiveResponse(Response response) + { + ResponseReceived?.Invoke(response); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Response.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Response.cs new file mode 100755 index 0000000..3050345 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Response.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRun.Models +{ + public enum ResponseStatus + { + Success = 0, + Unsupported, + Fail + } + + public class Response + { + public string Data { get; } + public string TransID { get; } + public ResponseStatus Status { get; } + + public Response(string tid, string data, ResponseStatus status) + { + TransID = tid; + Data = data; + Status = status; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/ISettingManager.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/ISettingManager.cs new file mode 100755 index 0000000..02c9d39 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/ISettingManager.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Settings +{ + public interface ISettingManager + { + void Initialize(Action next); + + event Action GpsStateChanged; + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingManager.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingManager.cs new file mode 100755 index 0000000..b60fb96 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingManager.cs @@ -0,0 +1,168 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Settings +{ + public enum GPSState + { + Off, + Ready, + DisabledForActivity, + Acquiring, + Acquired, + Error + } + + public class SettingManager : ISettingManager + { + readonly IDispatcher _dispatcher; + public event Action GpsStateChanged; + NotificationObserver _gpsStateObserver; + bool isGpsEnabled = false; + + private GPSState _gpsState = GPSState.Off; + public GPSState GpsState + { + get + { + if (!isGpsEnabled) + { + return GPSState.Off; + } + else + { + return _gpsState; + } + } + set + { + if (_gpsState == value) + return; + if (!isGpsEnabled) + { + _gpsState = GPSState.Off; + } else + { + _gpsState = value; + } + + GpsStateChanged?.Invoke(_gpsState); + } + } + + public SettingManager(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + } + + public void Initialize(Action next) + { + _gpsStateObserver = new NotificationObserver(OperationType.GpsStateChanged); + _gpsStateObserver.NotificationReceived += (string data) => + { + Console.WriteLine("Gps Changed Received"); + try + { + if (Int32.Parse(data) == 0) + GpsState = GPSState.Acquiring; + else + GpsState = GPSState.Acquired; + } + catch + { + Console.WriteLine("Fail to parse notification"); + } + }; + + var monitor = _dispatcher.Listen(OperationType.GpsStateChanged); + _gpsStateObserver.Subscribe(monitor); + + var request = new Request(OperationType.GetSettings, ""); + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Receive Get Settings"); + if (response.Status == ResponseStatus.Success) + OnReceiveSettingsDataString(response.Data); + + next?.Invoke(); + }; + _dispatcher.SendRequest(request); + } + + void OnReceiveSettingsDataString(string jsonString) + { + SettingsData settingsData = ParseSettingsDataJson(jsonString); + if (settingsData == null) + return; + + if (settingsData.gps != null) + { + if (settingsData.gps.isEnabled && settingsData.gps.systemValue) + { + isGpsEnabled = true; + GpsState = GPSState.Ready; + } + else + { + isGpsEnabled = false; + GpsState = GPSState.Off; + } + } + } + + public SettingsData ParseSettingsDataJson(string jsonString) + { + SettingsData settingsData= null; + try + { + settingsData = JsonConvert.DeserializeObject(jsonString); + } + catch + { + Console.WriteLine($"Settings DATA JSON Parse Fail: {jsonString}"); + } + return settingsData; + } + + private void StartGpsAcquire() + { + var request = new Request(OperationType.StartGpsAcquire, ""); + request.ResponseReceived += (Response response) => + { + if (response.Status == ResponseStatus.Success) + { + GpsState = GPSState.Acquiring; + } + }; + _dispatcher.SendRequest(request); + } + + private void StopGpsAcquire() + { + var request = new Request(OperationType.StopGpsAcquire, ""); + request.ResponseReceived += (Response response) => + { + if (response.Status == ResponseStatus.Success) + { + GpsState = GPSState.Off; + } + }; + _dispatcher.SendRequest(request); + } + + public void ToggleGPS(bool gps) + { + isGpsEnabled = gps; + + if (!isGpsEnabled) + { + StopGpsAcquire(); + } else + { + StartGpsAcquire(); + } + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingsData.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingsData.cs new file mode 100755 index 0000000..222d728 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Settings/SettingsData.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Settings +{ + public class SettingsData + { + public GpsSettingData gps { get; set; } + public DebugSettingData debug { get; set; } + public ScreenSettingData screenTimeout { get; set; } + } + + public class GpsSettingData + { + public bool systemValue { get; set; } + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } + + public class DebugSettingData + { + public FileLogger fileLogger { get; set; } + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } + + public class ScreenSettingData + { + public int value { get; set; } + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } + + public class FileLogger + { + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageCadence.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageCadence.cs new file mode 100755 index 0000000..82d2c46 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageCadence.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class AverageCadence : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.AvgCadence; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.cadence.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "label_cadence_avg"; + case WorkoutItemLabelType.ShortLabel: + return "label_cadence_avg_short"; + case WorkoutItemLabelType.MediumLabel: + return "label_cadence_avg_medium"; + case WorkoutItemLabelType.LongLabel: + return "label_cadence_avg_long"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_steps_per_min"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.aggregates == null || data.aggregates.cadence_avg <= 0) + return; + + var newValue = ConvertValue(data.aggregates.cadence_avg); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"AverageCadence Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedCadence = value; + var readableValue = convertedCadence > 0 ? convertedCadence.ToString() : "--"; + return readableValue; + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageHr.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageHr.cs new file mode 100755 index 0000000..a77cfb2 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageHr.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class AverageHr : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.AvgHR; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.hr.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "AVG HR"; + case WorkoutItemLabelType.ShortLabel: + return "AVG HR"; + case WorkoutItemLabelType.MediumLabel: + return "AVG HR"; + case WorkoutItemLabelType.LongLabel: + return "AVG HEART RATE(BPM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "bpm"; + } + + public void OnDataReceived(WorkoutData data) + { + var newValue = data.hrData.avg.ToString(); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"AverageHr Data Changed: {_value}"); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AveragePace.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AveragePace.cs new file mode 100755 index 0000000..7be76ee --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AveragePace.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class AveragePace : IWorkoutItem + { + string _value = "--"; + const int MAX_PACE = 3600; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.AvgPace; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.pace.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "label_pace_avg"; + case WorkoutItemLabelType.ShortLabel: + return "label_pace_avg_short"; + case WorkoutItemLabelType.MediumLabel: + return "label_pace_avg_medium"; + case WorkoutItemLabelType.LongLabel: + return "label_pace_avg_long"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_min_per_km"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.aggregates == null || data.aggregates.active_time_total < 0 || data.aggregates.distance_total < 0) + return; + + var newValue = ConvertValue(data.aggregates.active_time_total / data.aggregates.distance_total); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"AveragePace Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedPace = Math.Round(value); + var readableValue = convertedPace > 0 && convertedPace <= MAX_PACE ? convertedPace.ToString() : "--"; + return readableValue; + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageSpeed.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageSpeed.cs new file mode 100755 index 0000000..e8767ec --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/AverageSpeed.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class AverageSpeed : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.AvgSpeed; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.pace.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "label_speed_avg"; + case WorkoutItemLabelType.ShortLabel: + return "label_speed_avg_short"; + case WorkoutItemLabelType.MediumLabel: + return "label_speed_avg_medium"; + case WorkoutItemLabelType.LongLabel: + return "label_speed_avg_long"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_km_per_hr"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.aggregates == null || data.aggregates.active_time_total < 0 || data.aggregates.distance_total < 0) + return; + + var newValue = ConvertValue(data.aggregates.distance_total / data.aggregates.active_time_total); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"AverageSpeed Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedSpeed = Math.Round(value * 100) / 100; + var readableValue = convertedSpeed > 0 ? convertedSpeed.ToString() : "--"; + return readableValue; + } + + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Calories.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Calories.cs new file mode 100755 index 0000000..37b5dab --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Calories.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class Calories : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.Calories; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.calories.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "Calories"; + case WorkoutItemLabelType.ShortLabel: + return "Calories"; + case WorkoutItemLabelType.MediumLabel: + return "Calories"; + case WorkoutItemLabelType.LongLabel: + return "Calories"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return ""; + } + + public string GetValue() + { + return _value; + } + + public void OnDataReceived(WorkoutData data) + { + if (_value.Equals(data.calories)) + return; + + _value = data.calories; + ValueChanged?.Invoke(_value); + Console.WriteLine($"Calories Data Changed: {_value}"); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentCadence.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentCadence.cs new file mode 100755 index 0000000..8a8d848 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentCadence.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class CurrentCadence : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.Cadence; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.cadence.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "CADENCE"; + case WorkoutItemLabelType.ShortLabel: + return "CADENCE"; + case WorkoutItemLabelType.MediumLabel: + return "CADENCE"; + case WorkoutItemLabelType.LongLabel: + return "CADENCE(SPM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_steps_per_min"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.cadence == null || data.cadence.latest < 0) + return; + + var newValue = ConvertValue(data.cadence.latest); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"CurrentCadence Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedCadence = value; + var readableValue = convertedCadence > 0 ? convertedCadence.ToString() : "--"; + return readableValue; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentHr.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentHr.cs new file mode 100755 index 0000000..eae0b9c --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentHr.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class CurrentHr : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.CurrentHR; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.hr.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + //TODO : i18n + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "HR"; + case WorkoutItemLabelType.ShortLabel: + return "HR"; + case WorkoutItemLabelType.MediumLabel: + return "HR"; + case WorkoutItemLabelType.LongLabel: + return "HEART RATE(BPM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "bpm"; + } + + public void OnDataReceived(WorkoutData data) + { + var newValue = data.hrData.latest.ToString(); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"CurrentHr Data Changed: {_value}"); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentPace.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentPace.cs new file mode 100755 index 0000000..0f96530 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentPace.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class CurrentPace : IWorkoutItem + { + string _value = "--"; + const int MAX_PACE = 3600; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.Pace; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.pace.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "PACE"; + case WorkoutItemLabelType.ShortLabel: + return "PACE"; + case WorkoutItemLabelType.MediumLabel: + return "PACE"; + case WorkoutItemLabelType.LongLabel: + return "PACE(MIN/KM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_min_per_km"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.speed == null || data.speed.latest < 0) + return; + + var newValue = ConvertValue(data.speed.latest); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"CurrentPace Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedPace = Math.Round(value); + var readableValue = convertedPace > 0 && convertedPace <= MAX_PACE ? convertedPace.ToString() : "--"; + return readableValue; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentSpeed.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentSpeed.cs new file mode 100755 index 0000000..f8a4014 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/CurrentSpeed.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class CurrentSpeed : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.Speed; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.pace.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "label_speed"; + case WorkoutItemLabelType.ShortLabel: + return "label_speed_short"; + case WorkoutItemLabelType.MediumLabel: + return "label_speed_medium"; + case WorkoutItemLabelType.LongLabel: + return "label_speed_long"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_km_per_hr"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.speed.latest < 0) + return; + + var newValue = ConvertValue(data.speed.latest); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"CurrentSpeed Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedSpeed = Math.Round(value * 100) / 100; + var readableValue = convertedSpeed > 0 ? convertedSpeed.ToString() : "--"; + return readableValue; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Distance.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Distance.cs new file mode 100755 index 0000000..0e42071 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Distance.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class Distance : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.Distance; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.distance.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "DISTANCE"; + case WorkoutItemLabelType.ShortLabel: + return "DISTANCE"; + case WorkoutItemLabelType.MediumLabel: + return "DISTANCE"; + case WorkoutItemLabelType.LongLabel: + return "DISTANCE(KM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_km"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data == null || data.aggregates == null) + return; + + var newValue = data.aggregates.distance_total.ToString(); + + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"Distance Data Changed: {_value}"); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Duration.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Duration.cs new file mode 100755 index 0000000..7e20838 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/Duration.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class Duration : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.Duration; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.duration.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "DURATION"; + case WorkoutItemLabelType.ShortLabel: + return "DURATION"; + case WorkoutItemLabelType.MediumLabel: + return "DURATION"; + case WorkoutItemLabelType.LongLabel: + return "DURATION"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return null; + } + + public void OnDataReceived(WorkoutData data) + { + if (data == null || data.aggregates == null) + return; + + var newValue = data.aggregates.active_time_total.ToString(); + + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"Duration Data Changed: {_value}"); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/IWorkoutItem.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/IWorkoutItem.cs new file mode 100755 index 0000000..2392867 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/IWorkoutItem.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + public enum WorkoutItemType + { + Duration, + Distance, + Pace, + AvgPace, + MaxPace, + Speed, + AvgSpeed, + MaxSpeed, + Cadence, + AvgCadence, + StrideLength, + AvgStrideLength, + Steps, + Calories, + CurrentHR, + HRZone, + AvgHR, + PeakHR, + Intensity, + WillPower, + CadenceTargetRange, + StrideTargetRange, + CadenceRangeRate, + DistanceTargetRange, + DurationTargetRange, + PaceTargetRange, + PaceRangeRate, + SpeedTargetRange, + SpeedRangeRate + } + + public enum WorkoutItemLabelType + { + GeneralLabel, + ShortLabel, + MediumLabel, + LongLabel + } + + public interface IWorkoutItem + { + WorkoutItemType GetWorkoutItemType(); + + string GetImage(); + + string GetLabel(WorkoutItemLabelType type); + + string GetEmptyValue(); + + string GetUnit(); + + string GetStatsStyle(); + + string GetLabelStyle(); + + void OnDataReceived(WorkoutData data); + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxPace.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxPace.cs new file mode 100755 index 0000000..57d1813 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxPace.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class MaxPace : IWorkoutItem + { + string _value = "--"; + const int MAX_PACE = 3600; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.MaxPace; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.pace.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "MAX PACE"; + case WorkoutItemLabelType.ShortLabel: + return "MAX PACE"; + case WorkoutItemLabelType.MediumLabel: + return "MAX PACE"; + case WorkoutItemLabelType.LongLabel: + return "MAX PACE(MIN/KM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_min_per_km"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.aggregates.speed_max <= 0) + return; + + var newValue = ConvertValue(1 / data.aggregates.speed_max); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"MaxPace Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedPace = Math.Round(value); + var readableValue = convertedPace > 0 && convertedPace <= MAX_PACE ? convertedPace.ToString() : "--"; + return readableValue; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxSpeed.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxSpeed.cs new file mode 100755 index 0000000..db9b6fa --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/MaxSpeed.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class MaxSpeed : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.MaxSpeed; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.pace.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "label_speed_max"; + case WorkoutItemLabelType.ShortLabel: + return "label_speed_max_short"; + case WorkoutItemLabelType.MediumLabel: + return "label_speed_max_medium"; + case WorkoutItemLabelType.LongLabel: + return "label_speed_max_long"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "workout_km_per_hr"; + } + + public void OnDataReceived(WorkoutData data) + { + if (data.aggregates ==null || data.aggregates.speed_max < 0) + return; + + var newValue = ConvertValue(data.aggregates.speed_max); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"MaxSpeed Data Changed: {_value}"); + } + + string ConvertValue(float value) + { + var convertedSpeed = Math.Round(value * 100) / 100; + var readableValue = convertedSpeed > 0 ? convertedSpeed.ToString() : "--"; + return readableValue; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/PeakHr.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/PeakHr.cs new file mode 100755 index 0000000..183c335 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/PeakHr.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class PeakHr : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.PeakHR; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.hr.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "MAX HR"; + case WorkoutItemLabelType.ShortLabel: + return "MAX HR"; + case WorkoutItemLabelType.MediumLabel: + return "MAX HR"; + case WorkoutItemLabelType.LongLabel: + return "MAX HEART RATE(BPM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "bpm"; + } + + public void OnDataReceived(WorkoutData data) + { + var newValue = data.hrData.max.ToString(); + if (_value.Equals(newValue)) + return; + + _value = newValue; + ValueChanged?.Invoke(_value); + Console.WriteLine($"PeakHr Data Changed: {_value}"); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/WorkoutItemFactory.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/WorkoutItemFactory.cs new file mode 100755 index 0000000..c37d957 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/WorkoutItemFactory.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + public class WorkoutItemFactory + { + public static IWorkoutItem CreateWorkoutItem(WorkoutItemType type) + { + Console.WriteLine($"Create WorkoutItem {type}"); + switch (type) + { + case WorkoutItemType.Cadence: + return new CurrentCadence(); + case WorkoutItemType.AvgCadence: + return new AverageCadence(); + case WorkoutItemType.Pace: + return new CurrentPace(); + case WorkoutItemType.AvgPace: + return new AveragePace(); + case WorkoutItemType.MaxPace: + return new MaxPace(); + case WorkoutItemType.Distance: + return new Distance(); + case WorkoutItemType.Speed: + return new CurrentSpeed(); + case WorkoutItemType.AvgSpeed: + return new AverageSpeed(); + case WorkoutItemType.MaxSpeed: + return new MaxSpeed(); + case WorkoutItemType.AvgHR: + return new AverageHr(); + case WorkoutItemType.CurrentHR: + return new CurrentHr(); + case WorkoutItemType.PeakHR: + return new PeakHr(); + case WorkoutItemType.Calories: + return new Calories(); + case WorkoutItemType.Duration: + return new Duration(); + case WorkoutItemType.HRZone: + return new ZoneHr(); + default: + return null; + } + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/ZoneHr.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/ZoneHr.cs new file mode 100755 index 0000000..48b7c06 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Items/ZoneHr.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout.Items +{ + class ZoneHr : IWorkoutItem + { + string _value = "--"; + public event Action ValueChanged; + public string Value + { + get + { + return _value; + } + } + + public WorkoutItemType GetWorkoutItemType() + { + return WorkoutItemType.HRZone; + } + + public string GetEmptyValue() + { + return "--"; + } + + public string GetImage() + { + return "/assets/workout.hr.png"; + } + + public string GetLabel(WorkoutItemLabelType type) + { + switch (type) + { + case WorkoutItemLabelType.GeneralLabel: + default: + return "ZONE"; + case WorkoutItemLabelType.ShortLabel: + return "ZONE"; + case WorkoutItemLabelType.MediumLabel: + return "ZONE"; + case WorkoutItemLabelType.LongLabel: + return "ZONE HEART RATE(BPM)"; + } + } + + public string GetLabelStyle() + { + return ""; + } + + public string GetStatsStyle() + { + return ""; + } + + public string GetUnit() + { + return "bpm"; + } + + public void OnDataReceived(WorkoutData data) + { + if (_value.Equals(data.hrZone)) + return; + + _value = data.hrZone; + ValueChanged?.Invoke(_value); + Console.WriteLine($"hrZone Data Changed: {_value}"); + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Workout.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Workout.cs new file mode 100755 index 0000000..e4f022b --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/Workout.cs @@ -0,0 +1,329 @@ +using MapMyRun.Models.Workout.Items; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout +{ + public class Workout + { + bool _isProgressing = false; + bool _isListening = false; + WorkoutStatus _status = WorkoutStatus.NotStarted; + IDispatcher _dispatcher; + IPermissionManager _permissionManager; + NotificationObserver _workingDataObserver; + Dictionary _workoutItems = new Dictionary(); + + public event Action CaloriesDataReceived; + public event Action WillpowerDataReceived; + public event Action HrZoneDataReceived; + public event Action IntensityDataReceived; + public event Action AggregatesDataReceived; + public event Action CadenceDataReceived; + public event Action HrDataReceived; + public event Action SpeedDataReceived; + public event Action StrideLengthDataReceived; + public event Action FormCoachDataReceived; + public event Action GoalCoachDataReceived; + public event Action SpeedCoachDataReceived; + + enum WorkoutStatus + { + NotStarted, + Preparing, + InProgress, + Paused, + Finished, + Saved + } + + public Workout(IDispatcher dispatcher, IPermissionManager permissionManager) + { + _dispatcher = dispatcher; + _permissionManager = permissionManager; + } + + public void PrepareWorkout(int activity, bool gpsNeeded, bool shoesNeeded, bool cadenceNeeded) + { + if (_isProgressing || _status != WorkoutStatus.NotStarted) + return; + + _isProgressing = true; + + //TODO : Create items + //TEST CODE + ClearAllWorkoutItem(); + AddItem(WorkoutItemType.AvgHR); + AddItem(WorkoutItemType.PeakHR); + AddItem(WorkoutItemType.CurrentHR); + AddItem(WorkoutItemType.AvgPace); + AddItem(WorkoutItemType.MaxPace); + AddItem(WorkoutItemType.Pace); + AddItem(WorkoutItemType.AvgSpeed); + AddItem(WorkoutItemType.MaxSpeed); + AddItem(WorkoutItemType.Speed); + AddItem(WorkoutItemType.Cadence); + AddItem(WorkoutItemType.AvgCadence); + AddItem(WorkoutItemType.Distance); + AddItem(WorkoutItemType.Duration); + AddItem(WorkoutItemType.Calories); + AddItem(WorkoutItemType.HRZone); + + _permissionManager.RequestPermission(PermissionType.Sensor, (isSensorSuccess, isSensorGranted) => + { + if (!isSensorSuccess) + { + PrepareWorkout(false, false); + return; + } + + if (!gpsNeeded) + { + PrepareWorkout(isSensorGranted, false); + return; + } + + _permissionManager.RequestPermission(PermissionType.Location, (isGpsSuccess, isGpsGranted) => + { + if (!isGpsSuccess) + { + PrepareWorkout(isSensorGranted, false); + return; + } + + PrepareWorkout(isSensorGranted, isGpsGranted); + }); + }); + } + + void PrepareWorkout(bool isSensorsGranted, bool isLocationGranted) + { + var prepareData = CreatDummyPrepareWorkoutData(isSensorsGranted, isLocationGranted); + + var request = new Request(OperationType.PrepareWKO, prepareData); + + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get PrepareWKO Response"); + _status = WorkoutStatus.Preparing; + _isProgressing = false; + }; + _dispatcher.SendRequest(request); + } + + public void StartWorkout() + { + if (_isProgressing || _status != WorkoutStatus.Preparing) + return; + + _isProgressing = true; + + Console.WriteLine("Start Workout!"); + + var request = new Request(OperationType.StartWKO, "{\"activity_type\": 16}"); + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get StartWKO Response"); + _status = WorkoutStatus.InProgress; + _isProgressing = false; + }; + _dispatcher.SendRequest(request); + } + + public void FinishWorkout() + { + var request = new Request(OperationType.FinishWKO, "{\"activity_type\": 16}"); + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"Get FinishWKO Response"); + _status = WorkoutStatus.NotStarted; + _isListening = false; + _isProgressing = false; + }; + + _dispatcher.SendRequest(request); + } + + public void StartListenWorkoutData() + { + if (_isListening) + return; + + _isListening = true; + + _workingDataObserver = new NotificationObserver(OperationType.WkoDataChanged); + _workingDataObserver.NotificationReceived += (string data) => + { + WorkoutData workoutData = ParseWorkoutDataJson(data); + + if (workoutData != null) + PropagateWorkoutData(workoutData); + }; + + var monitor = _dispatcher.Listen(OperationType.WkoDataChanged); + _workingDataObserver.Subscribe(monitor); + } + + public WorkoutData ParseWorkoutDataJson(string jsonString) + { + WorkoutData workoutData = null; + try + { + workoutData = JsonConvert.DeserializeObject(jsonString); + } + catch + { + Console.WriteLine($"W.O DATA JSON Parse Fail: {jsonString}"); + } + return workoutData; + } + + public void PropagateWorkoutData(WorkoutData workoutData) + { + if (workoutData.calories != null) + { + CaloriesDataReceived?.Invoke(workoutData); + } + if (workoutData.willpower != null) + { + WillpowerDataReceived?.Invoke(workoutData); + } + if (workoutData.hrData != null) + { + HrDataReceived?.Invoke(workoutData); + } + if (workoutData.hrZone != null) + { + HrZoneDataReceived?.Invoke(workoutData); + } + if (workoutData.intensity != null) + { + IntensityDataReceived?.Invoke(workoutData); + } + if (workoutData.aggregates != null) + { + AggregatesDataReceived?.Invoke(workoutData); + } + if (workoutData.cadence != null) + { + CadenceDataReceived?.Invoke(workoutData); + } + if (workoutData.speed != null) + { + SpeedDataReceived?.Invoke(workoutData); + } + if (workoutData.strideLength != null) + { + StrideLengthDataReceived?.Invoke(workoutData); + } + if (workoutData.formCoach != null) + { + FormCoachDataReceived?.Invoke(workoutData); + } + if (workoutData.goalCoach != null) + { + GoalCoachDataReceived?.Invoke(workoutData); + } + if (workoutData.speedCoach != null) + { + SpeedCoachDataReceived?.Invoke(workoutData); + } + } + + public void AddItem(WorkoutItemType type) + { + if (_workoutItems.ContainsKey((int)type)) + return; + + var item = WorkoutItemFactory.CreateWorkoutItem(type); + + if (item == null) + return; + + _workoutItems.Add((int)type, item); + + SetWorkoutItemEvent(item, true); + } + + void SetWorkoutItemEvent(IWorkoutItem item, bool on) + { + var type = item.GetWorkoutItemType(); + + switch (type) + { + case WorkoutItemType.Cadence: + if (on) + CadenceDataReceived += item.OnDataReceived; + else + CadenceDataReceived -= item.OnDataReceived; + break; + case WorkoutItemType.Speed: + case WorkoutItemType.Pace: + if (on) + SpeedDataReceived += item.OnDataReceived; + else + SpeedDataReceived -= item.OnDataReceived; + break; + case WorkoutItemType.Duration: + case WorkoutItemType.AvgCadence: + case WorkoutItemType.AvgPace: + case WorkoutItemType.MaxPace: + case WorkoutItemType.Distance: + case WorkoutItemType.AvgSpeed: + case WorkoutItemType.MaxSpeed: + if (on) + AggregatesDataReceived += item.OnDataReceived; + else + AggregatesDataReceived -= item.OnDataReceived; + break; + case WorkoutItemType.AvgHR: + case WorkoutItemType.CurrentHR: + case WorkoutItemType.PeakHR: + if (on) + HrDataReceived += item.OnDataReceived; + else + HrDataReceived -= item.OnDataReceived; + break; + case WorkoutItemType.Calories: + if (on) + CaloriesDataReceived += item.OnDataReceived; + else + CaloriesDataReceived -= item.OnDataReceived; + break; + case WorkoutItemType.HRZone: + if (on) + HrZoneDataReceived += item.OnDataReceived; + else + HrZoneDataReceived -= item.OnDataReceived; + break; + default: + break; + } + } + + public IWorkoutItem GetItem(WorkoutItemType type) + { + if (_workoutItems.TryGetValue((int)type, out IWorkoutItem item)) + return item; + + return null; + } + + public void ClearAllWorkoutItem() + { + foreach (var pair in _workoutItems) + { + SetWorkoutItemEvent(pair.Value, false); + } + _workoutItems.Clear(); + } + + string CreatDummyPrepareWorkoutData(bool isSensorsGranted, bool isLocationGranted) + { + // TODO : Implement with json parser + return "{\"id\":208,\"gps_needed\":false,\"shoes_needed\":false,\"cadence_needed\":true,\"distance_needed\":true,\"is_pace\":true,\"is_hr_needed\":true,\"form_coaching_type\":0}"; + } + } +} diff --git a/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/WorkoutData.cs b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/WorkoutData.cs new file mode 100755 index 0000000..3ae1e03 --- /dev/null +++ b/test/UnderArmour/MapMyRun/MapMyRun/Models/Workout/WorkoutData.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRun.Models.Workout +{ + public class WorkoutData + { + public string calories { get; set; } + public string willpower { get; set; } + public string hrZone { get; set; } + public string intensity { get; set; } + public AggregateData aggregates { get; set; } + public ActivityData cadence { get; set; } + public ActivityData hrData { get; set; } + public ActivityData speed { get; set; } + public ActivityData strideLength { get; set; } + public RangeCoachingResult formCoach { get; set; } + public RangeCoachingResult speedCoach { get; set; } + public FitnessCoachingResult goalCoach { get; set; } + } + + public class ActivityData + { + public float min { get; set; } + public float max { get; set; } + public float avg { get; set; } + public float latest { get; set; } + public string type { get; set; } + } + + public class AggregateData + { + public float distance_total { get; set; } + public float metabolic_energy_total { get; set; } + public float intensity_avg { get; set; } + public float willpower { get; set; } + public int active_time_total { get; set; } + public int elapsed_time_total { get; set; } + public int steps_total { get; set; } + public int heartrate_avg { get; set; } + public int heartrate_max { get; set; } + public float speed_max { get; set; } + public float speed_avg { get; set; } + public float cadence_avg { get; set; } + public float cadence_max { get; set; } + public float stride_length_avg { get; set; } + } + + public class RangeCoachingResult + { + public float targetMax { get; set; } + public float targetMin { get; set; } + public float currentValue { get; set; } + public int status { get; set; } + public bool notif { get; set; } + } + + public class FitnessCoachingResult + { + public float current { get; set; } + public float target { get; set; } + public float percentage { get; set; } + } + +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Test/AccountManagerTest.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/AccountManagerTest.cs new file mode 100755 index 0000000..faedd5d --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/AccountManagerTest.cs @@ -0,0 +1,82 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRunService.Models; +using System.Collections.Generic; +using MapMyRunService.Models.Account; +using SQLite; + +namespace MapMyRunService.Tests +{ + [TestFixture] + class AccountManagerTest + { + IDB testDb = new DB(); + Mock mockDispatcher = null; + + public interface IDbConnection + { + int CreateTable(); + } + + [SetUp] + public void Setup() + { + mockDispatcher = new Mock(); + } + + [Test] + public void StartListenGetData_Called_CallDispatcherListen() + { + var accountManager = new AccountManager(testDb, mockDispatcher.Object); + mockDispatcher.Setup(x => x.Listen(OperationType.GetData)).Returns(new NotificationProvider(OperationType.GetData)); + accountManager.StartListenGetData(); + + mockDispatcher.Verify(x => x.Listen(OperationType.GetData), Times.Once()); + } + + [Test] + public void StartListenGetData_ReceivedMessageWithUserProfileAction_CallSendReply() + { + var accountManager = new AccountManager(testDb, mockDispatcher.Object); + var notificationProvider = new NotificationProvider(OperationType.GetData); + Response receivedResponse = null; + mockDispatcher.Setup(x => x.Listen(OperationType.GetData)).Returns(notificationProvider); + mockDispatcher.Setup(x => x.SendReply(It.IsAny())).Callback(x => { + receivedResponse = x; + }); + accountManager.StartListenGetData(); + + Dictionary getDataUserProfile = + new Dictionary { { "transID", "banana" }, { "data", "{\"action\": 1}" } }; + notificationProvider.PostNotification(getDataUserProfile); + Assert.AreEqual(receivedResponse.Status, ResponseStatus.Success); + Assert.AreEqual(receivedResponse.TransID, "banana"); + + Dictionary getDataUpdatedUserProfile = + new Dictionary { { "transID", "orange" }, { "data", "{\"action\": 2}" } }; + notificationProvider.PostNotification(getDataUpdatedUserProfile); + Assert.AreEqual(receivedResponse.Status, ResponseStatus.Success); + Assert.AreEqual(receivedResponse.TransID, "orange"); + + mockDispatcher.Verify(x => x.SendReply(It.IsAny()), Times.Exactly(2)); + } + + [Test] + public void StartListenGetData_ReceivedMessageWithAnotherAction_CallSendReply() + { + var accountManager = new AccountManager(testDb, mockDispatcher.Object); + var notificationProvider = new NotificationProvider(OperationType.GetData); + mockDispatcher.Setup(x => x.Listen(OperationType.GetData)).Returns(notificationProvider); + mockDispatcher.Setup(x => x.SendReply(It.IsAny())); + accountManager.StartListenGetData(); + + Dictionary getDataUpdatedUserProfile = + new Dictionary { { "transID", "orange" }, { "data", "{\"action\": 3}" } }; + notificationProvider.PostNotification(getDataUpdatedUserProfile); + mockDispatcher.Verify(x => x.SendReply(It.IsAny()), Times.Never()); + } + } +} + + diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Test/DB.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/DB.cs new file mode 100755 index 0000000..5c3a46b --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/DB.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using MapMyRunService.Models; +using SQLite; +using SQLitePCL; + +namespace MapMyRunService.Tests +{ + public class DB : IDB + { + private SQLiteConnection dbConnection { get; set; } + + private const string databaseFileName = "MMRServiceTestDB.db3"; + private string databasePath; + + public DB() + { + raw.SetProvider(new SQLite3Provider_sqlite3()); + raw.FreezeProvider(true); + + databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), databaseFileName); + dbConnection = new SQLiteConnection(databasePath); + } + + public SQLiteConnection getConnection() + { + return dbConnection; + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Test/DispatcherTest.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/DispatcherTest.cs new file mode 100755 index 0000000..ec522f8 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/DispatcherTest.cs @@ -0,0 +1,177 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRunService.Models; +using System.Collections.Generic; + +namespace MapMyRunService.Tests +{ + [TestFixture] + class DispatcherTest + { + Mock mockMessagePortHandler = null; + + [SetUp] + public void Setup() + { + mockMessagePortHandler = new Mock(); + } + + [Test] + public void Initialize_Called_CallMessagePortHandlerConnect() + { + mockMessagePortHandler.Setup(x => x.Connect(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + + mockMessagePortHandler.Verify(x => x.Connect(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + } + + [Test] + public void Initialize_Called_AddOnReceiveMessage() + { + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + + mockMessagePortHandler.SetupAdd(m => m.MessageReceived += dispatcher.OnReceiveMessage); + + dispatcher.Initialize(mockMessagePortHandler.Object); + mockMessagePortHandler.VerifyAdd(m => m.MessageReceived += dispatcher.OnReceiveMessage, Times.Once()); + } + + [Test] + public void SendRequest_FirstCalled_CallMessagePortHandlerSend() + { + var requestData = "banana"; + var request = new Mock(OperationType.PrepareWKO, requestData); + Dictionary requestDataSet = null; + + mockMessagePortHandler.Setup(x => x.Send(It.IsAny>())) + .Callback>(arg => { + requestDataSet = arg; + } + ); + + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + + dispatcher.SendRequest(request.Object); + requestDataSet.TryGetValue(Dispatcher.OperationKey, out string operation); + requestDataSet.TryGetValue(Dispatcher.DataKey, out string data); + + mockMessagePortHandler.Verify(x => x.Send(It.IsAny>()), Times.Once()); + Assert.AreEqual(((int)OperationType.PrepareWKO).ToString(), operation); + Assert.AreEqual(requestData, data); + } + + [Test] + public void OnReceiveMessage_Called_CallCorrectRequestOnReceiveResponse() + { + string requestData = "orange"; + var request = new Request(OperationType.PrepareWKO, requestData); + bool isCalled = false; + request.ResponseReceived += (Response data) => + { + isCalled = true; + }; + + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var tid = dispatcher.SendRequest(request); + + var responseData = new Dictionary + { + { Dispatcher.TransIdKey, tid} + }; + + dispatcher.OnReceiveMessage(responseData); + Assert.IsTrue(isCalled); + } + + [Test] + public void OnReceiveMessage_Called_DontCallIncorrectRequestOnReceiveResponse() + { + string requestData = "orange"; + var request = new Request(OperationType.PrepareWKO, requestData); + var isCalled = false; + request.ResponseReceived += (Response data) => + { + isCalled = true; + }; + + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var tid = dispatcher.SendRequest(request); + + var responseData = new Dictionary + { + { Dispatcher.TransIdKey, tid + "Never"} + }; + + dispatcher.OnReceiveMessage(responseData); + Assert.IsFalse(isCalled); + } + + [Test] + public void OnReceiveMessage_WithOperation_DontCallRequestOnReceiveResponse() + { + string requestData = "orange"; + var request = new Request(OperationType.PrepareWKO, requestData); + var isCalled = false; + request.ResponseReceived += (Response data) => + { + isCalled = true; + }; + + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var tid = dispatcher.SendRequest(request); + + var responseData = new Dictionary + { + { Dispatcher.OperationKey, "kiwi" }, + { Dispatcher.TransIdKey, tid} + }; + + dispatcher.OnReceiveMessage(responseData); + Assert.IsFalse(isCalled); + } + + [Test] + public void OnReceiveMessage_WithoutOperation_ProvideCorrectNotification() + { + var isCalled = false; + var mockObserver = new NotificationObserver(OperationType.ApplicationInitialized); + mockObserver.NotificationReceived += (Dictionary data) => + { + isCalled = true; + }; + + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + + var monitor = dispatcher.Listen(OperationType.ApplicationInitialized); + mockObserver.Subscribe(monitor); + + Dictionary responseData = new Dictionary + { + { Dispatcher.TransIdKey, "orange"}, + { Dispatcher.DataKey, "apple"}, + { Dispatcher.OperationKey, "46"} + }; + dispatcher.OnReceiveMessage(responseData); + + Assert.IsTrue(isCalled); + } + + [Test] + public void Listen_Called_ReturnCorrectNotificationProvider() + { + var dispatcher = MapMyRunService.Models.Dispatcher.Instance; + dispatcher.Initialize(mockMessagePortHandler.Object); + var notificationMonitor = dispatcher.Listen(OperationType.ApplicationInitialized); + Assert.AreEqual(notificationMonitor.Type, OperationType.ApplicationInitialized); + } + + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Test/KeepAliveTest.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/KeepAliveTest.cs new file mode 100755 index 0000000..49f6f6f --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/KeepAliveTest.cs @@ -0,0 +1,50 @@ +using System; +using NUnit.Framework; +using Moq; +using MapMyRunService.Models; +using MapMyRunService.Models.KeepAlive; +using System.Collections.Generic; + +namespace MapMyRunService.Tests +{ + [TestFixture] + class KeepAliveTest + { + Mock mockDispatcher = null; + + [SetUp] + public void Setup() + { + mockDispatcher = new Mock(); + } + + [Test] + public void ListenExitApp_Called_CallDispatcherListen() + { + var mockTizenServiceApplication = new Mock(); + var keepAlive = new KeepAlive(mockDispatcher.Object, mockTizenServiceApplication.Object); + mockDispatcher.Setup(x => x.Listen(OperationType.ExitApp)).Returns(new NotificationProvider(OperationType.ExitApp)); + + keepAlive.Start(); + + mockDispatcher.Verify(x => x.Listen(OperationType.ExitApp), Times.Once()); + } + + [Test] + public void ListenExitApp_ReceivedMessage_CallExitAppication() + { + var mockTizenServiceApplication = new Mock(); + mockTizenServiceApplication.Setup(x => x.ExitApplication()); + var keepAlive = new KeepAlive(mockDispatcher.Object, mockTizenServiceApplication.Object); + var notificationProvider = new NotificationProvider(OperationType.ExitApp); + mockDispatcher.Setup(x => x.Listen(OperationType.ExitApp)).Returns(notificationProvider); + keepAlive.Start(); + Dictionary exit = new Dictionary { { "data", "exit" } }; + notificationProvider.PostNotification(exit); + mockTizenServiceApplication.Verify(x => x.ExitApplication(), Times.Once()); + } + + } +} + + diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Test/MapMyRunService.Tests.csproj b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/MapMyRunService.Tests.csproj new file mode 100755 index 0000000..1a055b0 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Test/MapMyRunService.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService.Tizen.csproj b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService.Tizen.csproj new file mode 100755 index 0000000..8a0fac6 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService.Tizen.csproj @@ -0,0 +1,24 @@ + + + + Exe + tizen40 + + + + portable + + + None + + + + + + + + + + + + diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService_App.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService_App.cs new file mode 100755 index 0000000..fdcae29 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/MapMyRunService_App.cs @@ -0,0 +1,85 @@ +using MapMyRunService.Models; +using MapMyRunService.Models.Settings; +using MapMyRunService.Models.KeepAlive; +using MapMyRunService.Tizen.Models; +using MapMyRunService.Tizen.Models.Settings; +using MapMyRunService.Tizen.Models.Database; +using MapMyRunService.Tizen.Services; +using System; +using Tizen.Applications; + +namespace MapMyRunService.Tizen +{ + class App : ServiceApplication + { + private string uiAppId = "org.tizen.example.MapMyRun"; + private IMessagePortHandler messagePortHandler; + private Loading loading; + private KeepAlive keepAlive; + private SettingsManager setting; + private DB database; + + protected override void OnCreate() + { + base.OnCreate(); + + WorkoutService.Instance.Initialize(); + database = new DB(); + messagePortHandler = new MessagePortHandler(); + Dispatcher.Instance.Initialize(messagePortHandler); + + setting = new SettingsManager(Dispatcher.Instance); + setting.initializeGPS(new GPSManager()); + setting.Initialize(); + + loading = new Loading(messagePortHandler, uiAppId, database, new AuthCache()); + loading.StartLoading(); + + keepAlive = new KeepAlive(Dispatcher.Instance, new TizenServiceApplication(this)); + keepAlive.Start(); + + Console.WriteLine($"MapMyRunService application started."); + } + + protected override void OnAppControlReceived(AppControlReceivedEventArgs e) + { + base.OnAppControlReceived(e); + } + + protected override void OnDeviceOrientationChanged(DeviceOrientationEventArgs e) + { + base.OnDeviceOrientationChanged(e); + } + + protected override void OnLocaleChanged(LocaleChangedEventArgs e) + { + base.OnLocaleChanged(e); + } + + protected override void OnLowBattery(LowBatteryEventArgs e) + { + base.OnLowBattery(e); + } + + protected override void OnLowMemory(LowMemoryEventArgs e) + { + base.OnLowMemory(e); + } + + protected override void OnRegionFormatChanged(RegionFormatChangedEventArgs e) + { + base.OnRegionFormatChanged(e); + } + + protected override void OnTerminate() + { + base.OnTerminate(); + } + + static void Main(string[] args) + { + App app = new App(); + app.Run(args); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/AuthCache.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/AuthCache.cs new file mode 100755 index 0000000..8b757cc --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/AuthCache.cs @@ -0,0 +1,77 @@ +using MapMyRunService.Models.Auth; +using System; +using Tizen.Applications; + +namespace MapMyRunService.Tizen.Models +{ + public class AuthCache : IAuthCache + { + private readonly string CLIENT_ID_KEY = "token_ClientId"; + private readonly string CLIENT_SECRET_KEY = "token_ClientSecret"; + private readonly string ACCESS_TOKEN_KEY = "token_AccessToken"; + private readonly string REFRESH_TOKEN_KEY = "token_RefreshToken"; + + public AuthCache() + { + } + + public bool GetToken(AuthenticationToken authToken) + { + if (Preference.Contains(CLIENT_ID_KEY) && Preference.Get(CLIENT_ID_KEY) == authToken.ClientId) + { + + authToken.ClientSecret = Preference.Get(CLIENT_SECRET_KEY); + authToken.AccessToken = Preference.Get(ACCESS_TOKEN_KEY); + authToken.RefreshToken = Preference.Get(REFRESH_TOKEN_KEY); + + Console.WriteLine($"[AuthCache] [Get] Using token in cache."); + return true; + } + + Console.WriteLine($"[AuthCache] No cached token found."); + + return false; + } + + public void SaveToken(AuthenticationToken authToken) + { + Preference.Set(CLIENT_ID_KEY, authToken.ClientId); + Preference.Set(CLIENT_SECRET_KEY, authToken.ClientSecret); + Preference.Set(ACCESS_TOKEN_KEY, authToken.AccessToken); + Preference.Set(REFRESH_TOKEN_KEY, authToken.RefreshToken); + + Console.WriteLine($"[AuthCache] Update token in cache:"); + logTokenCache(); + } + + public void RemoveToken() + { + Console.WriteLine($"[AuthCache] RemoveToken called."); + logTokenCache(); + + if (Preference.Contains(CLIENT_ID_KEY)) + { + Preference.Remove(CLIENT_ID_KEY); + Preference.Remove(CLIENT_SECRET_KEY); + Preference.Remove(ACCESS_TOKEN_KEY); + Preference.Remove(REFRESH_TOKEN_KEY); + + Console.WriteLine($"[AuthCache] [Remove] Success: {!Preference.Contains(CLIENT_ID_KEY)}"); + } + } + + private void logTokenCache() + { + if (Preference.Contains(CLIENT_ID_KEY)) + { + Console.WriteLine($"[AuthCache] ClientId: {Preference.Get(CLIENT_ID_KEY)}"); + Console.WriteLine($"[AuthCache] ClientSecret: {Preference.Get(CLIENT_SECRET_KEY)}"); + Console.WriteLine($"[AuthCache] AccessToken: {Preference.Get(ACCESS_TOKEN_KEY)}"); + Console.WriteLine($"[AuthCache] RefreshToken: {Preference.Get(REFRESH_TOKEN_KEY)}"); + } else + { + Console.WriteLine($"[AuthCache] No cached token."); + } + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Database/DB.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Database/DB.cs new file mode 100755 index 0000000..2676035 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Database/DB.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using MapMyRunService.Models; +using SQLite; +using SQLitePCL; +using Tizen.Applications; + +namespace MapMyRunService.Tizen.Models.Database +{ + public class DB : IDB + { + private SQLiteConnection dbConnection { get; set; } + + private const string databaseFileName = "MMRServiceDB.db3"; + private string databasePath; + + public DB() + { + Console.WriteLine($"[DB] Initialize DB"); + + raw.SetProvider(new SQLite3Provider_sqlite3()); + raw.FreezeProvider(true); + + databasePath = Path.Combine(Application.Current.DirectoryInfo.Data, databaseFileName); + dbConnection = new SQLiteConnection(databasePath); + } + + public SQLiteConnection getConnection() + { + return dbConnection; + } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/MessagePortHandler.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/MessagePortHandler.cs new file mode 100755 index 0000000..abe38ef --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/MessagePortHandler.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Tizen.Applications; +using Tizen.Applications.Messages; +using MapMyRunService.Models; +using ElmSharp; + +namespace MapMyRunService.Tizen.Models +{ + public class MessagePortHandler : IMessagePortHandler + { + private string _localPortName = ""; + private string _remotePortName = ""; + private string _remoteAppId = ""; + private bool _isSecureMode = false; + + private bool _isInit = false; + private bool _isPendingMessageSending = false; + private List> _pendingMessages = new List>(); + + private MessagePort messagePort; + + public event Action> MessageReceived = delegate { }; + + public void Connect(string localPort, string remoteAppId, string remotePort, bool secureMode) + { + _localPortName = localPort; + _remotePortName = remotePort; + _remoteAppId = remoteAppId; + _isSecureMode = secureMode; + + messagePort = new MessagePort(_localPortName, _isSecureMode); + messagePort.MessageReceived += OnReceive; + messagePort.Listen(); + } + + public void Disconnect() + { + _localPortName = ""; + _remotePortName = ""; + _remoteAppId = ""; + _isSecureMode = false; + messagePort.StopListening(); + messagePort.Dispose(); + messagePort = null; + } + + public void Send(Dictionary data) + { + if (!_isInit) + { + _pendingMessages.Add(data); + + if (!_isPendingMessageSending) + { + _isPendingMessageSending = true; + TrySendFirstMessage(); + } + return; + } + + SendMessage(data); + } + + public bool IsRemoteRunning(string remoteAppId, string remotePortName, bool secureMode) + { + try + { + var remotePort = new RemotePort(remoteAppId, remotePortName, secureMode); + bool isRunning = remotePort.IsRunning(); + Console.WriteLine($"Remote Port: {remoteAppId}, {remotePortName}, {isRunning}"); + + return isRunning; + } + catch (Exception ex) + { + Console.WriteLine("Exception - Message: " + ex.Message + ", source: " + ex.Source + ", " + ex.StackTrace); + return false; + } + } + + private void TrySendFirstMessage() + { + try + { + var remotePort = new RemotePort(_remoteAppId, _remotePortName, false); + bool isRunning = remotePort.IsRunning(); + Console.WriteLine($"Remote Port: {_remoteAppId}, {_remotePortName}, {isRunning}"); + + if (!isRunning) + { + StartRetrySendFirstMessage(); + return; + } + + foreach (Dictionary pendingMessage in _pendingMessages) + { + //TODO : Fail case, Exit? or Recovery? + SendMessage(pendingMessage); + } + + _isPendingMessageSending = false; + _isInit = true; + } + catch (Exception ex) + { + Console.WriteLine("Exception - Message: " + ex.Message + ", source: " + ex.Source + ", " + ex.StackTrace); + StartRetrySendFirstMessage(); + } + } + + void SendMessage(Dictionary data) + { + var bundle = new Bundle(); + Console.WriteLine($"________________________________"); + Console.WriteLine($"[Service] >>>>> Send Data: "); + foreach (var pair in data) + { + bundle.AddItem(pair.Key, pair.Value); + Console.WriteLine($"{{ Key: {pair.Key}, Value: {pair.Value} }}"); + } + + try + { + messagePort.Send(bundle, _remoteAppId, _remotePortName, _isSecureMode); + Console.WriteLine($"Message sent: {_remoteAppId}, {_remotePortName}, {_isSecureMode}"); + } + catch (Exception ex) + { + Console.WriteLine("Exception - Message: " + ex.Message + ", source: " + ex.Source + ", " + ex.StackTrace); + } + } + + private void StartRetrySendFirstMessage() + { + EcoreMainloop.AddTimer(0.5, () => + { + TrySendFirstMessage(); + return false; + }); + } + + private void OnReceive(object sender, MessageReceivedEventArgs e) + { + StringBuilder messageLog = new StringBuilder(); + Console.WriteLine($"________________________________"); + Console.WriteLine("[Service] Service application received a message"); + Console.WriteLine($"App ID: {e.Remote.AppId}"); + Console.WriteLine($"PortName: {e.Remote.PortName}"); + Console.WriteLine($"Trusted: {e.Remote.Trusted}"); + Bundle responseBundle = e.Message; + + Console.WriteLine($"[Service] Response Data <<<<< : "); + var response = new Dictionary(); + + foreach (string key in responseBundle.Keys) + { + if (responseBundle.TryGetItem(key, out string value)) + { + Console.WriteLine($"{{ Key: {key}, Value: {value} }}"); + response.Add(key, value); + } + } + + MessageReceived(response); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Settings/GPSManager.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Settings/GPSManager.cs new file mode 100755 index 0000000..686ed24 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/Settings/GPSManager.cs @@ -0,0 +1,38 @@ +using MapMyRunService.Models; +using MapMyRunService.Models.Settings; +using System; +using System.Collections.Generic; +using Tizen.Location; + +namespace MapMyRunService.Tizen.Models.Settings +{ + public class GPSManager : IGPSManager + { + public event EventHandler GPSChanged; + private Locator _locator; + + public GPSManager() + { + _locator = new Locator(LocationType.Gps); + _locator.ServiceStateChanged += (object s, ServiceStateChangedEventArgs e) => + { + GPSStateEventArgs args = new GPSStateEventArgs(); + if (e.ServiceState == ServiceState.Enabled) args.enabled = true; + GPSChanged?.Invoke(this, args); + }; + } + ~GPSManager() + { + _locator.Dispose(); + } + public void DisableGps() + { + _locator.Stop(); + } + + public void EnableGps() + { + _locator.Start(); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/TizenServiceApplication.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/TizenServiceApplication.cs new file mode 100755 index 0000000..41af805 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Models/TizenServiceApplication.cs @@ -0,0 +1,24 @@ +using MapMyRunService.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Tizen.Applications; + +namespace MapMyRunService.Tizen.Models +{ + class TizenServiceApplication : ITizenServiceApplication + { + readonly ServiceApplication _serviceApp; + + public TizenServiceApplication(ServiceApplication serviceApp) + { + _serviceApp = serviceApp; + } + + public void ExitApplication() + { + _serviceApp.Exit(); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/SensorService.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/SensorService.cs new file mode 100755 index 0000000..5082817 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/SensorService.cs @@ -0,0 +1,280 @@ +using MapMyRunService.Models; +using MapMyRunService.Tizen.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Tizen.Location; +using Tizen.Security; +using Tizen.Sensor; + +namespace MapMyRunService.Tizen.Services +{ + public class SensorService + { + private readonly string healthSensorPrivilege = "http://tizen.org/privilege/healthinfo"; + private readonly string gpsPrivilege = "http://tizen.org/privilege/location"; + + private HeartRateMonitor heartRateMonitor; + private Pedometer pedometerSensor; + + private Locator locator; + private bool locatorRunning = false; + + private bool runHealthRequested = false; + private bool runGpsRequested = false; + + + public HeartRateData CurrentHeartRateData { get; private set; } + public PedometerData CurrentPedometerData { get; private set; } + public LocationData CurrentLocationData { get; private set; } + + + private static SensorService _instance; + public static SensorService Instance + { + get + { + if (_instance == null) + _instance = new SensorService(); + + return _instance; + } + set + { + _instance = value; + } + } + + private SensorService() + { + CurrentHeartRateData = new HeartRateData(); + CurrentPedometerData = new PedometerData(); + CurrentLocationData = new LocationData(); + + CheckPermissionsThenInit(healthSensorPrivilege); + CheckPermissionsThenInit(gpsPrivilege); + } + + /// + /// Check sensor status + /// + /// + /// + public bool IsSensorRunning(SupportedSensor sensor) + { + switch (sensor) + { + case SupportedSensor.GPS: + return locator != null && locatorRunning; + case SupportedSensor.HeartRate: + return heartRateMonitor != null && heartRateMonitor.IsSensing; + case SupportedSensor.Pedometer: + return pedometerSensor != null && pedometerSensor.IsSensing; + } + + return false; + } + + private void InitializeHealthSensors() + { + Console.WriteLine($"Initialize Health Sensors (HR, Pedometer)."); + + if (heartRateMonitor == null && HeartRateMonitor.IsSupported) + { + heartRateMonitor = new HeartRateMonitor(); + heartRateMonitor.DataUpdated += new EventHandler(UpdateHeartRateData); + } + + if (pedometerSensor == null && Pedometer.IsSupported) + { + pedometerSensor = new Pedometer(); + pedometerSensor.DataUpdated += new EventHandler(UpdatePedometerData); + } + + if (runHealthRequested) + { + RunHealthSensors(); + } + } + + private void InitializeGPS() + { + Console.WriteLine($"Initialize GPS."); + if (locator == null) + { + locator = new Locator(LocationType.Gps); + locator.LocationChanged += new EventHandler(UpdateLocationData); + + if (runGpsRequested) + { + RunGPS(); + } + } + } + + public void RunHealthSensors() + { + if (heartRateMonitor != null && !heartRateMonitor.IsSensing) + { + heartRateMonitor.Start(); + Console.WriteLine($"HR Monitor started."); + } + + if (pedometerSensor != null && !pedometerSensor.IsSensing) + { + pedometerSensor.Start(); + Console.WriteLine($"Pedometer started."); + } + + // Unable to run health sensors since they are not yet initialized + if ((heartRateMonitor == null && pedometerSensor == null)) + { + runHealthRequested = true; + } else + { + runHealthRequested = false; + } + } + + public void RunGPS() + { + if (locator != null) + { + locator.Start(); + locatorRunning = true; + runGpsRequested = false; + + Console.WriteLine($"GPS Sensing started."); + } else + { + // unable to run gps despite request + runGpsRequested = true; + } + } + + public void StopSensor(SupportedSensor supportedSensor) + { + switch (supportedSensor) + { + case SupportedSensor.HeartRate: + if (HeartRateMonitor.IsSupported && heartRateMonitor.IsSensing) + { + heartRateMonitor.Stop(); + Console.WriteLine($"HR Monitor stopped."); + } + break; + case SupportedSensor.Pedometer: + if (Pedometer.IsSupported && pedometerSensor.IsSensing) + { + pedometerSensor.Stop(); + Console.WriteLine($"Pedometer stopped."); + } + break; + case SupportedSensor.GPS: + if (locatorRunning) + { + locatorRunning = false; + locator?.Stop(); + locator?.Dispose(); + Console.WriteLine($"GPS sensing stopped."); + } + break; + case SupportedSensor.All: + if (HeartRateMonitor.IsSupported && heartRateMonitor.IsSensing) + { + heartRateMonitor.Stop(); + Console.WriteLine($"HR Monitor stopped."); + } + if (Pedometer.IsSupported && pedometerSensor.IsSensing) + { + pedometerSensor.Stop(); + Console.WriteLine($"Pedometer stopped."); + } + if (locatorRunning) + { + locatorRunning = false; + locator?.Stop(); + // locator?.Dispose(); + Console.WriteLine($"GPS sensing stopped."); + } + break; + } + } + + private void UpdatePedometerData(object sender, PedometerDataUpdatedEventArgs e) + { + //if (CurrentPedometerData.StepCount != e.StepCount) + // Console.WriteLine($"Updated step count: {CurrentPedometerData.StepCount}"); + + CurrentPedometerData.StepCount = e.StepCount; + CurrentPedometerData.Distance = e.MovingDistance; + CurrentPedometerData.CaloriesBurned = e.CalorieBurned; + CurrentPedometerData.RunStepCount = e.RunStepCount; + CurrentPedometerData.WalkStepCount = e.WalkStepCount; + CurrentPedometerData.Speed = e.LastSpeed; + } + + private void UpdateHeartRateData(object sender, HeartRateMonitorDataUpdatedEventArgs e) + { + //if (CurrentHeartRateData.LastHeartRate != e.HeartRate) + // Console.WriteLine($"Updated HR: {CurrentHeartRateData.LastHeartRate}, Min: {CurrentHeartRateData.MinHeartRate}, Max: {CurrentHeartRateData.MaxHeartRate}"); + + CurrentHeartRateData.LastHeartRate = e.HeartRate; + } + + private void UpdateLocationData(object sender, LocationChangedEventArgs e) + { + //if (CurrentLocationData.Latitude != e.Location.Latitude || CurrentLocationData.Longitude != e.Location.Longitude) + // Console.WriteLine($"Updated location: Lat: {e.Location.Latitude}, Long: {e.Location.Longitude}"); + + + CurrentLocationData.Latitude = e.Location.Latitude; + CurrentLocationData.Longitude = e.Location.Longitude; + } + + private void CheckPermissionsThenInit(string privilege) + { + CheckResult result = PrivacyPrivilegeManager.CheckPermission(privilege); + + switch (result) + { + case CheckResult.Allow: + Console.WriteLine($"{privilege}: Allow"); + if (privilege == healthSensorPrivilege) + InitializeHealthSensors(); + else + InitializeGPS(); + break; + case CheckResult.Ask: + Console.WriteLine($"{privilege}: Ask"); + + PrivacyPrivilegeManager.RequestPermission(privilege); + break; + case CheckResult.Deny: + Console.WriteLine($"{privilege}: Deny"); + + throw new UnauthorizedAccessException($"Service has no permission to use {privilege}"); + } + + PrivacyPrivilegeManager.ResponseContext responseContext; + PrivacyPrivilegeManager.GetResponseContext(privilege).TryGetTarget(out responseContext); + responseContext.ResponseFetched += new EventHandler(PermissionUpdated); + } + + + void PermissionUpdated(object sender, RequestResponseEventArgs e) + { + Console.WriteLine($"Privilege {e.privilege} updated to: {e.result}"); + CheckPermissionsThenInit(e.privilege); + } + } + + public enum SupportedSensor + { + HeartRate, + Pedometer, + GPS, + All + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/WorkoutService.cs b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/WorkoutService.cs new file mode 100755 index 0000000..123266b --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/Services/WorkoutService.cs @@ -0,0 +1,104 @@ +using MapMyRunService.Models; +using MapMyRunService.Models.Workout; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Tizen.Services +{ + public class WorkoutService + { + private WorkoutManager workoutManager; + + private SensorService sensorService; + + public static WorkoutService Instance { get; private set; } = new WorkoutService(); + private WorkoutService() + { + sensorService = SensorService.Instance; + workoutManager = WorkoutManager.Instance; + } + + public void Initialize() + { + workoutManager.PrepareWKO += PrepareWorkout; + workoutManager.StartWKO += StartWorkout; + workoutManager.PauseWKO += PauseWorkout; + workoutManager.ResumeWKO += ResumeWorkout; + workoutManager.FinishWKO += EndWorkout; + workoutManager.ReadData += UpdateWorkoutData; + + Console.WriteLine($"Workout service initialized."); + } + + public void PrepareWorkout(WorkoutReport workoutReport) + { + Console.WriteLine($"[WorkoutService] Workout Prepared"); + + workoutReport.LocStart.Latitude = sensorService.CurrentLocationData.Latitude; + workoutReport.LocStart.Longitude = sensorService.CurrentLocationData.Longitude; + } + + public void StartWorkout(WorkoutReport workoutReport) + { + Console.WriteLine($"[WorkoutService] Workout Started"); + + workoutReport.StartTime = DateTime.UtcNow; + + sensorService.RunHealthSensors(); + sensorService.RunGPS(); + } + + public void EndWorkout(WorkoutReport workoutReport) + { + Console.WriteLine($"[WorkoutService] Workout Finished"); + + workoutReport.EndTime = DateTime.UtcNow; + workoutReport.LocEnd.Latitude = sensorService.CurrentLocationData.Latitude; + workoutReport.LocEnd.Longitude = sensorService.CurrentLocationData.Longitude; + + sensorService.StopSensor(SupportedSensor.All); + } + + public void PauseWorkout(WorkoutReport workoutReport) + { + Console.WriteLine($"[WorkoutService] Workout Paused"); + sensorService.StopSensor(SupportedSensor.All); + } + + public void ResumeWorkout(WorkoutReport workoutReport) + { + Console.WriteLine($"[WorkoutService] Workout Resumed"); + + sensorService.RunHealthSensors(); + sensorService.RunGPS(); + } + + public void UpdateWorkoutData(WorkoutReport workoutReport) + { + // Package workout data here + + WorkoutData workoutData = new WorkoutData(); + workoutData.calories = sensorService.CurrentPedometerData.CaloriesBurned.ToString(); + + AggregateData aggregateData = new AggregateData(); + aggregateData.distance_total = sensorService.CurrentPedometerData.Distance; + aggregateData.steps_total = (int) sensorService.CurrentPedometerData.StepCount; + aggregateData.heartrate_avg = sensorService.CurrentHeartRateData.AvgHeartRate; + aggregateData.heartrate_max = sensorService.CurrentHeartRateData.MaxHeartRate; + + workoutData.aggregates = aggregateData; + + ActivityData hrActivity = new ActivityData(); + hrActivity.min = sensorService.CurrentHeartRateData.MinHeartRate; + hrActivity.max = sensorService.CurrentHeartRateData.MaxHeartRate; + hrActivity.avg = sensorService.CurrentHeartRateData.AvgHeartRate; + hrActivity.latest = sensorService.CurrentHeartRateData.LastHeartRate; + + workoutReport.workoutData = workoutData; + workoutReport.workoutData.hrData = hrActivity; + } + + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/shared/res/MapMyRunService.png b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/shared/res/MapMyRunService.png new file mode 100755 index 0000000000000000000000000000000000000000..9f3cb98608134e7b9eb4beb7394fec9f03e40f63 GIT binary patch literal 10097 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 TJbOA100000NkvXXu0mjf?x(Kf literal 0 HcmV?d00001 diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/tizen-manifest.xml b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/tizen-manifest.xml new file mode 100755 index 0000000..e96c8aa --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.Tizen/tizen-manifest.xml @@ -0,0 +1,19 @@ + + + + + + MapMyRunService.png + + + + + http://tizen.org/privilege/healthinfo + http://tizen.org/privilege/location + + + + true + true + true + diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService.sln b/test/UnderArmour/MapMyRunService/MapMyRunService.sln new file mode 100755 index 0000000..d93b454 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService.sln @@ -0,0 +1,33 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapMyRunService.Tizen", "MapMyRunService.Tizen\MapMyRunService.Tizen.csproj", "{DACD098D-6BEB-491C-A91D-4FABB3D4490A}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "MapMyRunService", "MapMyRunService\MapMyRunService.shproj", "{7B89FD19-6DCD-4721-9000-FA612161C492}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapMyRunService.Tests", "MapMyRunService.Test\MapMyRunService.Tests.csproj", "{D8AF456A-10C8-4BFE-BB6B-0120221A071A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DACD098D-6BEB-491C-A91D-4FABB3D4490A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DACD098D-6BEB-491C-A91D-4FABB3D4490A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DACD098D-6BEB-491C-A91D-4FABB3D4490A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DACD098D-6BEB-491C-A91D-4FABB3D4490A}.Release|Any CPU.Build.0 = Release|Any CPU + {D8AF456A-10C8-4BFE-BB6B-0120221A071A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8AF456A-10C8-4BFE-BB6B-0120221A071A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8AF456A-10C8-4BFE-BB6B-0120221A071A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8AF456A-10C8-4BFE-BB6B-0120221A071A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C7B6B376-8277-40AC-83A5-4D7204FE8B38} + EndGlobalSection +EndGlobal diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.projitems b/test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.projitems new file mode 100755 index 0000000..e7fee0c --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.projitems @@ -0,0 +1,42 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 7b89fd19-6dcd-4721-9000-fa612161c492 + + + MapMyRunService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.shproj b/test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.shproj new file mode 100755 index 0000000..c4242e2 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/MapMyRunService.shproj @@ -0,0 +1,13 @@ + + + + 7b89fd19-6dcd-4721-9000-fa612161c492 + 14.0 + + + + + + + + diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/AccountManager.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/AccountManager.cs new file mode 100755 index 0000000..561db94 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/AccountManager.cs @@ -0,0 +1,92 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models.Account +{ + public class AccountManager + { + + private readonly IDispatcher _dispatcher; + private readonly IDB database; + private readonly UserDao userDao; + private const string GET_DATA_ACTION_KEY = "action"; + private User currentUser; + + private class GetDataContent + { + public int action; + } + + public AccountManager(IDB _database, IDispatcher dispatcher) + { + database = _database; + userDao = new UserDao(database); + _dispatcher = dispatcher; + currentUser = userDao.Get(""); + } + + public void StartListenGetData() + { + NotificationObserver getDataObserver = new NotificationObserver(OperationType.GetData); + getDataObserver.NotificationReceived += (Dictionary data) => + { + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.GetData notification"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + GetDataContent getDataContent = null; + try + { + getDataContent = JsonConvert.DeserializeObject(dataValue); + } + catch + { + Console.WriteLine("Get Data JSON Parse Fail"); + } + + if (DataActionType.GetUserProfile != (DataActionType)getDataContent.action + && DataActionType.GetUpdatedUserProfile != (DataActionType)getDataContent.action) + { + return; + } + + string dataJson = JsonConvert.SerializeObject(currentUser); + var response = new Response(transID, dataJson, ResponseStatus.Success); + + _dispatcher.SendReply(response); + }; + + var monitor = _dispatcher.Listen(OperationType.GetData); + getDataObserver.Subscribe(monitor); + } + + public void DeleteCurrentUser() + { + currentUser = userDao.Get(""); + + if (currentUser != null) + { + Console.WriteLine($"[AccountManager] Deleting user"); + userDao.Delete(currentUser); + } + currentUser = null; + } + + public void UpdateUser(User user) + { + currentUser = user; + userDao.Update(user); + } + + public void ChangeUser(User user) + { + DeleteCurrentUser(); + userDao.Insert(user); + currentUser = user; + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/User.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/User.cs new file mode 100755 index 0000000..655e1f9 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/User.cs @@ -0,0 +1,31 @@ +using SQLite; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models.Account +{ + public class User + { + [PrimaryKey] + public int id { get; set; } + public string first_name { get; set; } + public string last_name { get; set; } + public string display_name { get; set; } + public double weight { get; set; } + public double height { get; set; } + public string hobbies { get; set; } + public string date_joined { get; set; } + public string introduction { get; set; } + public string display_measurement_system { get; set; } + public string last_login { get; set; } + public string email { get; set; } + public string username { get; set; } + public string last_initial { get; set; } + public string preferred_language { get; set; } + public string gender { get; set; } + public string time_zone { get; set; } + public string birthdate { get; set; } + public string profile_statement { get; set; } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/UserDao.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/UserDao.cs new file mode 100755 index 0000000..8d078ca --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Account/UserDao.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models.Account +{ + public class UserDao + { + private IDB database; + private readonly int SAMPLE_ID = 12345678; + + public UserDao(IDB _database) + { + database = _database; + database.getConnection().CreateTable(); + + InsertSampleData(); // temporary + + Console.WriteLine($"[UserDao] Test DB Read:"); + var user = database.getConnection().Table().First(); + Console.WriteLine($"[UserDao] Username: {user.username}, email: {user.email}"); + } + + public User Get(string id) + { + if (id.Equals("")) + { + try + { + var currentUser = database.getConnection().Table().First(); + return currentUser; + } + catch (Exception) + { + Console.WriteLine($"[DB] Get Error: no user found"); + return null; + } + } + + try + { + var currentUser = database.getConnection().Query("select * from User where id = ?", id); + Console.WriteLine($"[DB] Get User: user size {currentUser.Count}"); + return currentUser[0]; + } + catch (Exception) + { + Console.WriteLine($"[DB] Get Error: no user found"); + return null; + } + } + + public void Insert(User user) + { + try + { + database.getConnection().Insert(user); + } + catch (Exception) + { + Console.WriteLine($"[DB] Get Error"); + } + } + + public void Update(User user) + { + try + { + database.getConnection().Update(user); + } + catch (Exception) + { + Console.WriteLine($"[DB] Update Error"); + } + } + + public void Delete(User user) + { + try + { + database.getConnection().Delete(user); + } + catch (Exception) + { + Console.WriteLine($"[DB] Delete Error: no user found"); + } + } + + public void InsertSampleData() + { + if (database.getConnection().Table().Count() == 0) + { + Console.WriteLine($"DB is empty. Adding sample data."); + + // add sample user + User sampleUser = new User + { + id = SAMPLE_ID, + first_name = "Sample", + last_name = "User", + display_name = "Sample", + weight = 75.55, + height = 1.75, + date_joined = "2019-09-20T04:51:23+00:00", + display_measurement_system = "metric", + last_login = "2019-09-20T04:51:23+00:00", + email = "sample_user@email.org", + username = "sample_user", + last_initial = "U", + preferred_language = "en-US", + gender = "M", + time_zone = "Asia/Seoul", + birthdate = "1940-02-25" + }; + + Insert(sampleUser); + } + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/Authentication.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/Authentication.cs new file mode 100755 index 0000000..5342d52 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/Authentication.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace MapMyRunService.Models.Auth +{ + public class Authentication + { + private static SemaphoreSlim RequestLock = new SemaphoreSlim(1, 1); + private const string ClientId = "ExampleClientID123"; + private const string ClientSecret = "ExampleClientSecret123"; + + private readonly IAuthenticationProvider authProvider; + private IAuthCache authCache; + + public AuthenticationToken AuthToken { get; set; } = null; + + public Authentication(IAuthenticationProvider authProvider, IAuthCache authCache) + { + AuthToken = new AuthenticationToken() + { + ClientId = ClientId, + ClientSecret = ClientSecret + }; + this.authProvider = authProvider; + this.authCache = authCache; + } + + public async Task RequestAuthentication() + { + if (AuthToken.AccessToken is null) + { + await RequestLock.WaitAsync(); + try + { + if (AuthToken.AccessToken is null) + { + Console.WriteLine("[Auth] Request auth tokens"); + + if (!authCache.GetToken(AuthToken)) + { + await authProvider.RequestAuthentication(AuthToken); + } + + if (AuthToken.AccessToken is null) + { + Console.WriteLine("[Auth] Authentication Failed"); + return false; + } + else + { + authCache.SaveToken(AuthToken); + Console.WriteLine("[Auth] Authentication Success"); + return true; + } + } + } + finally + { + RequestLock.Release(); + } + } + return true; + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/AuthenticationToken.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/AuthenticationToken.cs new file mode 100755 index 0000000..43d3272 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/AuthenticationToken.cs @@ -0,0 +1,11 @@ + +namespace MapMyRunService.Models.Auth +{ + public class AuthenticationToken + { + public string ClientId { get; set; } = null; + public string ClientSecret { get; set; } = null; + public string AccessToken { get; set; } = null; + public string RefreshToken { get; set; } = null; + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/ExampleAuthProvider.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/ExampleAuthProvider.cs new file mode 100755 index 0000000..7b22816 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/ExampleAuthProvider.cs @@ -0,0 +1,72 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace MapMyRunService.Models.Auth +{ + /// + /// ExampleAuthProvider uses "Type on watch" authentication method. + /// Although this authentication method requires a lot of typing on a small screen, + /// It provides the most simple model of user authentication. + /// Authentication providers should set up a login page that can be fully displayed on a wearable device screen. + /// + public sealed class ExampleAuthProvider : IAuthenticationProvider + { + private const string AuthServerUri = "http://example.org/authenticate/wearable"; + private const string AccessTokenKey = "access_token"; + private const string RefreshTokenKey = "refresh_token"; + + private class HttpResponseBody + { + public Dictionary json; + } + + public ExampleAuthProvider() + { + } + + public async Task RequestAuthentication(AuthenticationToken authToken) + { + try + { + HttpResponseMessage response = await HttpService.Instance.GetAsync(AuthServerUri); + + if (response.IsSuccessStatusCode) + { + Console.WriteLine("Example authentication request authentication success"); + string responseBodyString = await response.Content.ReadAsStringAsync(); + HttpResponseBody responseBody = JsonConvert.DeserializeObject(responseBodyString); + if (responseBody.json.ContainsKey(AccessTokenKey)) + { + if (responseBody.json.ContainsKey(RefreshTokenKey)) + { + authToken.AccessToken = responseBody.json[AccessTokenKey]; + authToken.RefreshToken = responseBody.json[RefreshTokenKey]; + } + else + { + Console.WriteLine($"HttpResponse does not have access token or access token property key does not match {RefreshTokenKey}"); + } + } + else + { + Console.WriteLine($"HttpResponse does not have access token or access token property key does not match {AccessTokenKey}"); + } + } + else + { + Console.WriteLine("Example authentication request authentication failed"); + } + } + catch (HttpRequestException e) + { + // Dummy response for testing with test URL + Console.WriteLine($"[Auth] Example authentication - using test authToken"); + authToken.AccessToken = "test"; + authToken.RefreshToken = "test"; + } + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthCache.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthCache.cs new file mode 100755 index 0000000..1c5dbf2 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthCache.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models.Auth +{ + public interface IAuthCache + { + bool GetToken(AuthenticationToken authToken); + + void SaveToken(AuthenticationToken authToken); + + void RemoveToken(); + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthenticationProvider.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthenticationProvider.cs new file mode 100755 index 0000000..49a0a4e --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Auth/IAuthenticationProvider.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace MapMyRunService.Models.Auth +{ + public interface IAuthenticationProvider + { + Task RequestAuthentication(AuthenticationToken authToken); + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Dispatcher.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Dispatcher.cs new file mode 100755 index 0000000..35ddc1a --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Dispatcher.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public class Dispatcher : IDispatcher + { + IMessagePortHandler _messagePortHandler = null; + private const string UIAppId = "org.tizen.example.MapMyRun"; + private const string LocalPort = "SERVICE_PORT"; + private const string RemotePort = "APP_PORT"; + public const string OperationKey = "operation"; + public const string DataKey = "data"; + public const string TransIdKey = "transID"; + public const string ResponseStatusKey = "status"; + private const bool SecureMode = false; + private uint TransIdIndex = 0; + private readonly Dictionary RequestCache = new Dictionary(); + private readonly List NotificationProviders = new List(); + + public static Dispatcher Instance { get; private set; } = new Dispatcher(); + + private Dispatcher() + { + } + + public void Initialize(IMessagePortHandler messagePortHandler) + { + _messagePortHandler = messagePortHandler; + _messagePortHandler.MessageReceived += OnReceiveMessage; + _messagePortHandler.Connect(LocalPort, UIAppId, RemotePort, SecureMode); + } + + public string SendRequest(Request request) + { + request.TransID = TransIdIndex.ToString(); + TransIdIndex++; + RequestCache.Add(request.TransID, request); + var dataSet = new Dictionary + { + { OperationKey, ((int)request.Operation).ToString() }, + { DataKey, request.Data }, + { TransIdKey, request.TransID } + }; + + _messagePortHandler.Send(dataSet); + return request.TransID; + } + + public string SendReply(Response response) + { + var dataSet = new Dictionary + { + { TransIdKey, response.TransID }, + { DataKey, response.Data }, + { ResponseStatusKey, response.Status.ToString()} + }; + + _messagePortHandler.Send(dataSet); + return response.TransID; + } + + public void OnReceiveMessage(Dictionary dataSet) + { + if (dataSet.TryGetValue(OperationKey, out string operation)) + { + Console.WriteLine($"[Service] Receive Notification: {operation}"); + + try + { + OperationType type = (OperationType)Int32.Parse(operation); + //if (dataSet.TryGetValue(DataKey, out string data)) + // PostNotification(data, type); + PostNotification(dataSet, type); + } + catch + { + Console.WriteLine("[Service] Invalid Operation"); + } + return; + } + + if (!dataSet.TryGetValue(TransIdKey, out string tid)) + { + Console.WriteLine("[Service] Request for operation response is not found"); + return; + } + + if (RequestCache.TryGetValue(tid, out Request cachedRequest)) + { + dataSet.TryGetValue(DataKey, out string data); + dataSet.TryGetValue(ResponseStatusKey, out string status); + ResponseStatus responseStatus; + try + { + responseStatus = (ResponseStatus)Int32.Parse(status); + } + catch + { + responseStatus = ResponseStatus.Fail; + } + + var response = new Response(tid, data, responseStatus); + cachedRequest.OnReceiveResponse(response); + } + } + + void PostNotification(Dictionary dataSet, OperationType type) + { + NotificationProvider notificationMonitor; + try + { + notificationMonitor = NotificationProviders.Find(x => x != null && x.Type == type); + notificationMonitor.PostNotification(dataSet); + } + catch + { + Console.WriteLine($"[Service] Notification Monitor none: {type}"); + } + } + + public bool IsRemoteRunning() + { + return _messagePortHandler.IsRemoteRunning(UIAppId, RemotePort, SecureMode); + } + + public NotificationProvider Listen(OperationType type) + { + NotificationProvider notificationMonitor; + try + { + notificationMonitor = NotificationProviders.Find(x => x != null && x.Type == type); + Console.WriteLine($"[Service] Listen exist Notification Monitor {notificationMonitor.Type}"); + return notificationMonitor; + } + catch + { + notificationMonitor = new NotificationProvider(type); + NotificationProviders.Add(notificationMonitor); + Console.WriteLine($"[Service] Create new Notification Monitor {notificationMonitor.Type}"); + return notificationMonitor; + } + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/HttpService.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/HttpService.cs new file mode 100755 index 0000000..88310eb --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/HttpService.cs @@ -0,0 +1,31 @@ +using System.Net.Http; + +namespace MapMyRunService.Models +{ + public sealed class HttpService : HttpClient + { + private static HttpService _instance; + private static readonly object INSTANCELOCK = new object(); + + private HttpService(){ + } + + public static HttpService Instance + { + get + { + if(_instance is null) + { + lock (INSTANCELOCK) + { + if(_instance is null) + { + _instance = new HttpService(); + } + } + } + return _instance; + } + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDB.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDB.cs new file mode 100755 index 0000000..8b51106 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDB.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using SQLite; + +namespace MapMyRunService.Models +{ + public interface IDB + { + SQLiteConnection getConnection(); + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDispatcher.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDispatcher.cs new file mode 100755 index 0000000..a20205a --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IDispatcher.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public interface IDispatcher + { + void Initialize(IMessagePortHandler messagePortHandler); + + string SendRequest(Request request); + + string SendReply(Response response); + + void OnReceiveMessage(Dictionary data); + + bool IsRemoteRunning(); + + NotificationProvider Listen(OperationType type); + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IMessagePortHandler.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IMessagePortHandler.cs new file mode 100755 index 0000000..b69e614 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/IMessagePortHandler.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public interface IMessagePortHandler + { + void Connect(string localPort, string remoteAppId, string remotePort, bool secureMode); + + bool IsRemoteRunning(string remoteAppId, string remotePort, bool secureMode); + + void Disconnect(); + + void Send(Dictionary data); + + event Action> MessageReceived; + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/ITizenServiceApplication.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/ITizenServiceApplication.cs new file mode 100755 index 0000000..afd5d79 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/ITizenServiceApplication.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models +{ + public interface ITizenServiceApplication + { + void ExitApplication(); + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/KeepAlive/KeepAlive.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/KeepAlive/KeepAlive.cs new file mode 100755 index 0000000..d32135f --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/KeepAlive/KeepAlive.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Timers; + +namespace MapMyRunService.Models.KeepAlive +{ + class KeepAlive + { + NotificationObserver _exitAppObserver; + readonly IDispatcher _dispatcher; + readonly ITizenServiceApplication _tizenServiceApp; + private Timer periodicUpdateTimer; + + public KeepAlive(IDispatcher dispatcher, ITizenServiceApplication tizenServiceApp) + { + _dispatcher = dispatcher; + _tizenServiceApp = tizenServiceApp; + } + + public void Start() + { + ListenExitApp(); + + periodicUpdateTimer = new Timer(5000); + periodicUpdateTimer.Elapsed += new ElapsedEventHandler((s, e) => { + Console.WriteLine("[Service] Check keep-alive"); + if (!_dispatcher.IsRemoteRunning()) + { + Console.WriteLine("UI Applicaions disconnected : ExitApp"); + _tizenServiceApp.ExitApplication(); + } + }); + periodicUpdateTimer.AutoReset = true; + periodicUpdateTimer.Enabled = true; + } + + void ListenExitApp() + { + _exitAppObserver = new NotificationObserver(OperationType.ExitApp); + _exitAppObserver.NotificationReceived += (Dictionary data) => + { + Console.WriteLine("Receive ExitApp"); + _tizenServiceApp.ExitApplication(); + }; + + var monitor = _dispatcher.Listen(OperationType.ExitApp); + _exitAppObserver.Subscribe(monitor); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Loading.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Loading.cs new file mode 100755 index 0000000..ce1d476 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Loading.cs @@ -0,0 +1,102 @@ +using MapMyRunService.Models.Auth; +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; +using System.Threading.Tasks; +using MapMyRunService.Models.Workout; +using MapMyRunService.Models.Account; + +namespace MapMyRunService.Models +{ + public class Loading + { + readonly string _uiAppId; + readonly IDispatcher _dispatcher; + readonly IMessagePortHandler _messagePortHandler; + private AccountManager accountManager; + private IDB _database; + private IAuthCache _authCache; + + public Loading(IMessagePortHandler messagePortHandler, string uiAppId, IDB database, IAuthCache authCache) + { + _messagePortHandler = messagePortHandler; + _dispatcher = Dispatcher.Instance; + _uiAppId = uiAppId; + _database = database; + _authCache = authCache; + } + + public void StartLoading() + { + InitializeMessageListeners(); + WorkoutManager.Instance.InitalizeWorkoutListeners(); + accountManager = new AccountManager(_database, _dispatcher); + accountManager.StartListenGetData(); + } + + /// + /// Listeners to UI app + /// + private void InitializeMessageListeners() + { + ListenAuth(); + ListenRequestPermission(); + } + + private void ListenAuth() + { + NotificationObserver checkAuthObserver = new NotificationObserver(OperationType.CheckIsAuthenticated); + checkAuthObserver.NotificationReceived += async (Dictionary data) => + { + + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.CheckIsAuthenticated notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + Authentication auth = new Authentication(new ExampleAuthProvider(), _authCache); + bool success = await auth.RequestAuthentication(); + if (success) + { + // Response payload(second parameter) is empty because the UI app doesn't have to know the access token. + // It just needs to know that the request was successful. + var response = new Response(transID, "", ResponseStatus.Success); + _dispatcher.SendReply(response); + } + else + { + Console.WriteLine("[Service] Authentication request was not successful"); + var response = new Response(transID, "", ResponseStatus.Fail); + _dispatcher.SendReply(response); + } + }; + + var monitor = _dispatcher.Listen(OperationType.CheckIsAuthenticated); + checkAuthObserver.Subscribe(monitor); + } + + private void ListenRequestPermission() + { + NotificationObserver prepareWkoObserver = new NotificationObserver(OperationType.RequestPermission); + prepareWkoObserver.NotificationReceived += (Dictionary data) => + { + + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.RequestPermission notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + // TOOD: Insert proper response here + var response = new Response(transID, "{\"data\": true}", ResponseStatus.Success); + + _dispatcher.SendReply(response); + }; + + var monitor = _dispatcher.Listen(OperationType.RequestPermission); + prepareWkoObserver.Subscribe(monitor); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationObserver.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationObserver.cs new file mode 100755 index 0000000..9be2970 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationObserver.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models +{ + public class NotificationObserver : IObserver> + { + public OperationType Type { get; } + public event Action> NotificationReceived; + public event Action NotificationErrorReceived; + IDisposable _cancellation; + + public NotificationObserver(OperationType type) + { + Type = type; + } + + public void Subscribe(NotificationProvider provider) + { + _cancellation = provider.Subscribe(this); + } + + public void Unsubscribe() + { + _cancellation.Dispose(); + } + + public void OnCompleted() + { + Console.WriteLine($"Notification Observer Completed Type: {Type}"); + } + + public void OnError(Exception error) + { + NotificationErrorReceived(error); + } + + public void OnNext(Dictionary value) + { + NotificationReceived(value); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationProvider.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationProvider.cs new file mode 100755 index 0000000..37a323b --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/NotificationProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models +{ + public class NotificationProvider : IObservable> + { + readonly List>> observers; + public OperationType Type { get; } + public string Name { get; } + + public NotificationProvider(OperationType type) + { + Type = type; + observers = new List>>(); + } + + public void PostNotification(Dictionary dataSet) + { + foreach (var observer in observers) + observer.OnNext(dataSet); + } + + public IDisposable Subscribe(IObserver> observer) + { + if (!observers.Contains(observer)) + observers.Add(observer); + return new Unsubscriber(observers, observer); + } + + private class Unsubscriber : IDisposable + { + private readonly List>> _observers; + private readonly IObserver> _observer; + + public Unsubscriber(List>> observers, IObserver> observer) + { + _observers = observers; + _observer = observer; + } + + public void Dispose() + { + if (_observer != null && _observers.Contains(_observer)) + _observers.Remove(_observer); + } + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Request.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Request.cs new file mode 100755 index 0000000..e079086 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Request.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public enum OperationType + { + PrepareWKO = 1, + StartWKO, + PauseWKO, + ResumeWKO, + FinishWKO, + SaveWKO, + DiscardWKO, + CheckSContext, + CheckIsLoggedIn, + NavigateToLogin, + NavigateToProfile, + CheckIsAuthenticated, + CheckIsInstalled, + GetData, + UpdateNutritionPreference, + GetSleepScore, + ExitApp, + MFPGetCurrentSteps, + MFPAddEnergy, + MFPAddWater, + GetSettings, + SetSettings, + GetWorkoutSettings, + SetWorkoutSettings, + UnfinishedWorkoutRestore, + RestoreWKO, + StartGpsAcquire, + StopGpsAcquire, + CheckNetworkConnection, + ChangeNutritionSource, + GetConsentStatus, + GetUserGear, + ConnectUserGear, + DisconnectUserGear, + RequestPermission, + GetTodayTrainingPlans, + // More operations to be here + OperationMax = GetTodayTrainingPlans, + // Notification Type Start + WkoDataChanged, + HRData, + AggregatesData, + HRZone, + IntensityChanged, + GetDiarySummary, + GetCachedDiarySummary, + StepsChanged, + SapInitialized, + ApplicationInitialized, + ApplicationStarted, + NoBluetoothConnection, + WristUpEvent, + MfpDataUpdated, + SapRemoteServiceNotFound, + WillpowerChanged, + EnergyChanged, + SpeedChanged, + GpsStateChanged, + ShoesStateChanged, + MeasurementPreferenceChanged, + NutritionPreferencesUpdated, + SleepScoreReceived, + WorkoutSyncSuccess, + WorkoutSyncFailed, + UserProfileUpdated, + LocationChanged, + Logout, + SettingsChanged, + WorkoutSettingsChanged, + SapConnectionTimeout, + LangChanged, + HapticFeedback, + NutritionSourceSupportChanged, + CadenceChanged, + StrideLengthChanged, + RTFCUpdate, + RTFCNotification, + RTSCUpdate, + RTSCNotification, + GoalUpdate, + GoalCompleted, + AppStateForeground, + AppStateBackground, + // More operations to be here + NotificationTypeMax = AppStateBackground + } + + public enum DataActionType + { + None = 0, + GetUserProfile, + GetUpdatedUserProfile, + PostWorkout, + GetUserGoals, + GetUpToDateUserGoals, + GetHrZones, + GetChallenges, + GetChallengeDetails, + GetWorkouts + } + + public class Request + { + public OperationType Operation { get; } + public string Data { get; } + public event Action ResponseReceived; + public string TransID { get; set; } + + public Request (OperationType operation, string data) + { + Operation = operation; + Data = data; + } + + public void OnReceiveResponse(Response response) + { + ResponseReceived?.Invoke(response); + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Response.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Response.cs new file mode 100755 index 0000000..901f351 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Response.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public enum ResponseStatus + { + Success = 0, + Unsupported, + Fail + } + + public class Response + { + public string Data { get; } + public string TransID { get; } + public ResponseStatus Status { get; } + + public Response(string tid, string data, ResponseStatus status) + { + TransID = tid; + Data = data; + Status = status; + } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/IGPSManager.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/IGPSManager.cs new file mode 100755 index 0000000..a5f2fc8 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/IGPSManager.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models.Settings +{ + public class GPSStateEventArgs : EventArgs + { + public bool enabled; + public GPSStateEventArgs() + { + enabled = false; + } + } + + public interface IGPSManager + { + event EventHandler GPSChanged; + void EnableGps(); + void DisableGps(); + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsData.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsData.cs new file mode 100755 index 0000000..2847f91 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsData.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models.Settings +{ + public class SettingsData + { + public GpsSettingData gps { get; set; } + public DebugSettingData debug { get; set; } + public ScreenSettingData screenTimeout { get; set; } + } + + public class GpsSettingData + { + public bool systemValue { get; set; } + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } + + public class DebugSettingData + { + public FileLogger fileLogger { get; set; } + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } + + public class ScreenSettingData + { + public int value { get; set; } + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } + + public class FileLogger + { + public bool isEnabled { get; set; } + public bool isSupported { get; set; } + } +} \ No newline at end of file diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsManager.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsManager.cs new file mode 100755 index 0000000..f0737b8 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Settings/SettingsManager.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace MapMyRunService.Models.Settings +{ + public class SettingsManager + { + private IDispatcher _dispatcher; + private IGPSManager _gpsmanager; + private SettingsData _settings; + + public SettingsManager(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + _settings = new SettingsData(); + _gpsmanager = null; + } + + public void initializeGPS(IGPSManager gpsmanager) + { + _gpsmanager = gpsmanager; + gpsmanager.GPSChanged += (object s, GPSStateEventArgs e) => + { + GpsSettingData gps = new GpsSettingData(); + gps.isEnabled = e.enabled; + _settings.gps = gps; + }; + } + + public void Initialize() + { + ListenStartGpsAcquire(); + ListenStopGpsAcquire(); + + ListenGetSettings(); + } + + public void ListenGetSettings() + { + NotificationObserver getSettingsObserver = new NotificationObserver(OperationType.GetSettings); + + getSettingsObserver.NotificationReceived += (Dictionary data) => + { + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.GetSettings notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + string settingsDataJsonString = JsonConvert.SerializeObject(_settings); + _dispatcher.SendReply(new Response(transID, settingsDataJsonString, ResponseStatus.Success)); + + }; + + var monitor = _dispatcher.Listen(OperationType.GetSettings); + getSettingsObserver.Subscribe(monitor); + } + + public void ListenStartGpsAcquire() + { + NotificationObserver startGpsAcquire = new NotificationObserver(OperationType.StartGpsAcquire); + + startGpsAcquire.NotificationReceived += (Dictionary data) => + { + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.StartGpsAcquire notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + if(_gpsmanager is null) + { + Console.WriteLine("GPS Manager is not set"); + _dispatcher.SendReply(new Response(transID, "", ResponseStatus.Fail)); + } + else + { + try + { + _gpsmanager.EnableGps(); + _dispatcher.SendReply(new Response(transID, "", ResponseStatus.Success)); + } + catch + { + Console.WriteLine("Error in gps manager"); + _dispatcher.SendReply(new Response(transID, "", ResponseStatus.Fail)); + } + } + }; + + var monitor = _dispatcher.Listen(OperationType.StartGpsAcquire); + startGpsAcquire.Subscribe(monitor); + } + + public void ListenStopGpsAcquire() + { + NotificationObserver stopGpsAcquire = new NotificationObserver(OperationType.StopGpsAcquire); + + stopGpsAcquire.NotificationReceived += (Dictionary data) => + { + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.StopGpsAcquire notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + if (_gpsmanager is null) + { + Console.WriteLine("GPS Manager is not set"); + _dispatcher.SendReply(new Response(transID, "", ResponseStatus.Fail)); + } + else + { + try + { + _gpsmanager.DisableGps(); + _dispatcher.SendReply(new Response(transID, "", ResponseStatus.Success)); + } + catch + { + Console.WriteLine("Error in gps manager"); + _dispatcher.SendReply(new Response(transID, "", ResponseStatus.Fail)); + } + } + }; + + var monitor = _dispatcher.Listen(OperationType.StopGpsAcquire); + stopGpsAcquire.Subscribe(monitor); + } + + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/HeartRateData.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/HeartRateData.cs new file mode 100755 index 0000000..093cf6b --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/HeartRateData.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public class HeartRateData + { + private int _lastHeartRate = -1; + public int LastHeartRate + { + get + { + return _lastHeartRate; + } + set + { + MinHeartRate = value <= 0 ? 0 + : value < MinHeartRate ? value : MinHeartRate; + MaxHeartRate = value > MaxHeartRate ? value : MaxHeartRate; + + _lastHeartRate = value <= 0 ? 0 : value; + AvgHeartRate = value; + } + } + + private int hrDataPoints { get; set; } = 0; + private int _avgHeartRate { get; set; } = 0; + public int AvgHeartRate + { + get + { + + return _avgHeartRate / (hrDataPoints < 1 ? 1 : hrDataPoints); + } + set + { + if (value <= 0) return; + + _avgHeartRate += value; + hrDataPoints++; + } + } + + public int MinHeartRate { get; set; } = 10000; + public int MaxHeartRate { get; set; } = -1; + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/LocationData.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/LocationData.cs new file mode 100755 index 0000000..3a8ac74 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/LocationData.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public class LocationData + { + public double Latitude { get; set; } = 0; + public double Longitude { get; set; } = 0; + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/PedometerData.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/PedometerData.cs new file mode 100755 index 0000000..10cb926 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/PedometerData.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public class PedometerData + { + public uint StepCount { get; set; } = 0; + public uint WalkStepCount { get; set; } = 0; + public uint RunStepCount { get; set; } = 0; + public float CaloriesBurned { get; set; } = 0; + public float Distance { get; set; } = 0; + public float Speed { get; set; } = 0; + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutData.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutData.cs new file mode 100755 index 0000000..0ba852b --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutData.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MapMyRunService.Models.Workout +{ + public class WorkoutData + { + public string calories { get; set; } + public string willpower { get; set; } + public string hrZone { get; set; } + public string intensity { get; set; } + public AggregateData aggregates { get; set; } + public ActivityData cadence { get; set; } + public ActivityData hrData { get; set; } + public ActivityData speed { get; set; } + public ActivityData strideLength { get; set; } + public RangeCoachingResult formCoach { get; set; } + public RangeCoachingResult speedCoach { get; set; } + public FitnessCoachingResult goalCoach { get; set; } + } + + public class ActivityData + { + public float min { get; set; } + public float max { get; set; } + public float avg { get; set; } + public float latest { get; set; } + public string type { get; set; } + } + + public class AggregateData + { + public float distance_total { get; set; } + public float metabolic_energy_total { get; set; } + public float intensity_avg { get; set; } + public float willpower { get; set; } + public int active_time_total { get; set; } + public int elapsed_time_total { get; set; } + public int steps_total { get; set; } + public int heartrate_avg { get; set; } + public int heartrate_max { get; set; } + public float speed_max { get; set; } + public float speed_avg { get; set; } + public float cadence_avg { get; set; } + public float cadence_max { get; set; } + public float stride_length_avg { get; set; } + } + + public class RangeCoachingResult + { + public float targetMax { get; set; } + public float targetMin { get; set; } + public float currentValue { get; set; } + public int status { get; set; } + public bool notif { get; set; } + } + + public class FitnessCoachingResult + { + public float current { get; set; } + public float target { get; set; } + public float percentage { get; set; } + } +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutManager.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutManager.cs new file mode 100755 index 0000000..47c5dcd --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutManager.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Timers; +using Newtonsoft.Json; + + +namespace MapMyRunService.Models.Workout +{ + public class WorkoutManager + { + private IDispatcher _dispatcher; + + public event Action PrepareWKO = delegate { }; + public event Action StartWKO = delegate { }; + public event Action PauseWKO = delegate { }; + public event Action FinishWKO = delegate { }; + public event Action ResumeWKO = delegate { }; + public event Action ReadData = delegate { }; + + private Timer periodicUpdateTimer; + private bool shouldSendUpdates; + private double updateInProgress; // 0 - false, 1 - true + private WorkoutReport currentWorkoutReport; + + public static WorkoutManager Instance { get; private set; } = new WorkoutManager(); + private WorkoutManager() + { + _dispatcher = Dispatcher.Instance; + InitializePeriodicReporter(); + } + + private void InitializePeriodicReporter() + { + updateInProgress = 0; + shouldSendUpdates = false; + periodicUpdateTimer = new Timer(1000); + periodicUpdateTimer.Elapsed += new ElapsedEventHandler((s, e) => { UpdateWKOData(); }); + periodicUpdateTimer.AutoReset = true; + } + + private void disablePeriodicReporter() + { + shouldSendUpdates = false; + periodicUpdateTimer.Enabled = false; + } + + private void enablePeriodicReporter() + { + shouldSendUpdates = true; + periodicUpdateTimer.Enabled = true; + } + + public void InitalizeWorkoutListeners() + { + ListenPrepareWKO(); + ListenStartWKO(); + ListenPauseWKO(); + ListenFinishWKO(); + ListenResumeWKO(); + } + + private void ListenPrepareWKO() + { + currentWorkoutReport = new WorkoutReport(); + + NotificationObserver prepareWkoObserver = new NotificationObserver(OperationType.PrepareWKO); + prepareWkoObserver.NotificationReceived += (Dictionary data) => + { + + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.PrepareWKO notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + // TOOD: Insert proper response here + var response = new Response(transID, "{\"data\": 42}", ResponseStatus.Success); + + _dispatcher.SendReply(response); + + PrepareWKO?.Invoke(currentWorkoutReport); + }; + + var monitor = _dispatcher.Listen(OperationType.PrepareWKO); + prepareWkoObserver.Subscribe(monitor); + } + + private void ListenStartWKO() + { + NotificationObserver prepareWkoObserver = new NotificationObserver(OperationType.StartWKO); + prepareWkoObserver.NotificationReceived += (Dictionary data) => + { + + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.StartWKO notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + // TOOD: Insert proper response here + var response = new Response(transID, "{\"data\": 42}", ResponseStatus.Success); + + _dispatcher.SendReply(response); + + StartWKO?.Invoke(currentWorkoutReport); + + enablePeriodicReporter(); + }; + + var monitor = _dispatcher.Listen(OperationType.StartWKO); + prepareWkoObserver.Subscribe(monitor); + } + + private void ListenFinishWKO() + { + NotificationObserver prepareWkoObserver = new NotificationObserver(OperationType.FinishWKO); + prepareWkoObserver.NotificationReceived += (Dictionary data) => + { + + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.FinishWKO notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + // TOOD: Insert proper response here + var response = new Response(transID, "{\"data\": 42}", ResponseStatus.Success); + + disablePeriodicReporter(); + _dispatcher.SendReply(response); + FinishWKO?.Invoke(currentWorkoutReport); + }; + + var monitor = _dispatcher.Listen(OperationType.FinishWKO); + prepareWkoObserver.Subscribe(monitor); + } + + private void ListenResumeWKO() + { + NotificationObserver prepareWkoObserver = new NotificationObserver(OperationType.ResumeWKO); + prepareWkoObserver.NotificationReceived += (Dictionary data) => + { + + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType.ResumeWKO notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + // TOOD: Insert proper response here + var response = new Response(transID, "{\"data\": 42}", ResponseStatus.Success); + + enablePeriodicReporter(); + _dispatcher.SendReply(response); + ResumeWKO?.Invoke(currentWorkoutReport); + }; + + var monitor = _dispatcher.Listen(OperationType.ResumeWKO); + prepareWkoObserver.Subscribe(monitor); + } + + private void ListenPauseWKO() + { + NotificationObserver prepareWkoObserver = new NotificationObserver(OperationType.PauseWKO); + prepareWkoObserver.NotificationReceived += (Dictionary data) => + { + + data.TryGetValue("transID", out string transID); + data.TryGetValue("data", out string dataValue); + + Console.WriteLine($"[Service] OperationType..PauseWKO notif"); + Console.WriteLine($"transID: {transID}, value: {dataValue}"); + + // TOOD: Insert proper response here + var response = new Response(transID, "{\"data\": 42}", ResponseStatus.Success); + + disablePeriodicReporter(); + _dispatcher.SendReply(response); + PauseWKO?.Invoke(currentWorkoutReport); + }; + + var monitor = _dispatcher.Listen(OperationType.PauseWKO); + prepareWkoObserver.Subscribe(monitor); + } + + private void UpdateWKOData() + { + if (shouldSendUpdates && + System.Threading.Interlocked.CompareExchange(ref updateInProgress, 1, 0) == 0) + { + Console.WriteLine("START Update WKO Data >>>>>"); + ReadData?.Invoke(currentWorkoutReport); + + string jsonReport = JsonConvert.SerializeObject(currentWorkoutReport.workoutData); + + var request = new Request(OperationType.WkoDataChanged, jsonReport); + request.ResponseReceived += (Response response) => + { + Console.WriteLine($"[Service] UpdateWKOData acknowledged."); + }; + _dispatcher.SendRequest(request); + + updateInProgress = 0; + } + } + } + +} diff --git a/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutReport.cs b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutReport.cs new file mode 100755 index 0000000..1e1c952 --- /dev/null +++ b/test/UnderArmour/MapMyRunService/MapMyRunService/Models/Workout/WorkoutReport.cs @@ -0,0 +1,25 @@ +using MapMyRunService.Models.Workout; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MapMyRunService.Models +{ + public class WorkoutReport + { + public DateTime StartTime { get; set; } + public int TimePaused { get; set; } + public DateTime EndTime { get; set; } + public LocationData LocStart { get; set; } + public LocationData LocEnd { get; set; } + + public WorkoutData workoutData { get; set; } + + public WorkoutReport() + { + LocStart = new LocationData(); + LocEnd = new LocationData(); + } + } +} -- 2.7.4