diff --git a/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml b/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml index caf2534..e533523 100644 --- a/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml +++ b/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + diff --git a/LastenradBayern/TINK.iOS/Info.plist b/LastenradBayern/TINK.iOS/Info.plist index a093c07..931aba9 100644 --- a/LastenradBayern/TINK.iOS/Info.plist +++ b/LastenradBayern/TINK.iOS/Info.plist @@ -56,8 +56,8 @@ CFBundleDisplayName LastenradBayern CFBundleVersion - 370 + 371 CFBundleShortVersionString - 3.0.370 + 3.0.371 diff --git a/LastenradBayern/TINK/View/Account/AccountPage.xaml.cs b/LastenradBayern/TINK/View/Account/AccountPage.xaml.cs index d470451..273a178 100644 --- a/LastenradBayern/TINK/View/Account/AccountPage.xaml.cs +++ b/LastenradBayern/TINK/View/Account/AccountPage.xaml.cs @@ -1,4 +1,4 @@ -using TINK.ViewModel; +using TINK.ViewModel; using Xamarin.Forms; using Xamarin.Forms.Xaml; using System.Threading.Tasks; @@ -150,7 +150,7 @@ namespace TINK.View.Account #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } -} \ No newline at end of file +} diff --git a/LastenradBayern/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs b/LastenradBayern/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs index e8b8131..b6a1239 100644 --- a/LastenradBayern/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs +++ b/LastenradBayern/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs @@ -251,7 +251,7 @@ namespace TINK.View.BikesAtStation /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } } diff --git a/LastenradBayern/TINK/View/Contact/ContactPage.xaml.cs b/LastenradBayern/TINK/View/Contact/ContactPage.xaml.cs index accc94c..5707962 100644 --- a/LastenradBayern/TINK/View/Contact/ContactPage.xaml.cs +++ b/LastenradBayern/TINK/View/Contact/ContactPage.xaml.cs @@ -1,4 +1,4 @@ -using Serilog; +using Serilog; using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; @@ -28,8 +28,11 @@ namespace TINK.View.Contact { InitializeComponent(); + var l_oModel = App.ModelRoot; + ViewModel = new ContactPageViewModel( App.ModelRoot.Flavor.GetDisplayName(), + l_oModel, () => App.CreateAttachment(), () => DependencyService.Get().OpenUrl(DependencyService.Get().StoreUrl), this); @@ -120,7 +123,7 @@ namespace TINK.View.Contact #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif #if USEFLYOUT @@ -131,4 +134,4 @@ namespace TINK.View.Contact public INavigationMasterDetail NavigationMasterDetail { set; private get; } #endif } -} \ No newline at end of file +} diff --git a/LastenradBayern/TINK/View/Contact/SelectStationPage.xaml.cs b/LastenradBayern/TINK/View/Contact/SelectStationPage.xaml.cs index c9a7d51..f4b04f9 100644 --- a/LastenradBayern/TINK/View/Contact/SelectStationPage.xaml.cs +++ b/LastenradBayern/TINK/View/Contact/SelectStationPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; #if USEFLYOUT using TINK.View.MasterDetail; @@ -116,7 +116,7 @@ namespace TINK.View.Contact #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif #if USEFLYOUT @@ -206,4 +206,4 @@ namespace TINK.View.Contact } } } -} \ No newline at end of file +} diff --git a/LastenradBayern/TINK/View/FeedbackPopup.xaml.cs b/LastenradBayern/TINK/View/FeedbackPopup.xaml.cs index bd1b32f..b217f81 100644 --- a/LastenradBayern/TINK/View/FeedbackPopup.xaml.cs +++ b/LastenradBayern/TINK/View/FeedbackPopup.xaml.cs @@ -12,7 +12,7 @@ namespace TINK.View /// Object holding info about battery. For some batteries charging level might need to be updated by user. /// Co2 saving information. public FeedbackPopup( - IBattery battery = null, + IBatteryMutable battery = null, string co2Saving = null) { InitializeComponent(); diff --git a/LastenradBayern/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml.cs b/LastenradBayern/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml.cs index b09973f..d8cae06 100644 --- a/LastenradBayern/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml.cs +++ b/LastenradBayern/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml.cs @@ -1,3 +1,4 @@ +using System.Globalization; using Serilog; using TINK.ViewModel; using TINK.ViewModel.Contact; @@ -36,6 +37,7 @@ namespace TINK.View.Contact App.ModelRoot.ResourceUrls.FeesResourcePath, App.ModelRoot.ResourceUrls.BikesResourcePath, App.ModelRoot.IsSiteCachingOn, + CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, () => App.ModelRoot.GetConnector(App.ModelRoot.GetIsConnected()).Query, resourceUrls => App.ModelRoot.ResourceUrls = resourceUrls); diff --git a/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs b/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs index ba9d70e..47827cf 100644 --- a/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs +++ b/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs @@ -181,7 +181,7 @@ namespace TINK.View.FindBike /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } } diff --git a/LastenradBayern/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs b/LastenradBayern/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs index 9af80a4..d20bf33 100644 --- a/LastenradBayern/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs +++ b/LastenradBayern/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs @@ -123,7 +123,7 @@ namespace TINK.View.Info.BikeInfo #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } } diff --git a/LastenradBayern/TINK/View/Login/LoginPage.xaml.cs b/LastenradBayern/TINK/View/Login/LoginPage.xaml.cs index b1e85fb..59fc351 100644 --- a/LastenradBayern/TINK/View/Login/LoginPage.xaml.cs +++ b/LastenradBayern/TINK/View/Login/LoginPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; using TINK.Model.Device; @@ -117,7 +117,7 @@ namespace TINK.View.Login #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } -} \ No newline at end of file +} diff --git a/LastenradBayern/TINK/View/Map/MapPage.xaml.cs b/LastenradBayern/TINK/View/Map/MapPage.xaml.cs index 1663f17..e5f5d84 100644 --- a/LastenradBayern/TINK/View/Map/MapPage.xaml.cs +++ b/LastenradBayern/TINK/View/Map/MapPage.xaml.cs @@ -121,7 +121,7 @@ namespace TINK.View.Map #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif #if USEFLYOUT diff --git a/LastenradBayern/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs b/LastenradBayern/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs index f231968..be38a16 100644 --- a/LastenradBayern/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs +++ b/LastenradBayern/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; using TINK.ViewModel.MiniSurvey; @@ -94,7 +94,7 @@ namespace TINK.View.MiniSurvey /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } -} \ No newline at end of file +} diff --git a/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs b/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs index fca7a06..4bc4681 100644 --- a/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs +++ b/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs @@ -181,7 +181,7 @@ namespace TINK.View.MyBikes /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } } diff --git a/LastenradBayern/TINK/View/Settings/SettingsPage.xaml.cs b/LastenradBayern/TINK/View/Settings/SettingsPage.xaml.cs index 00d3271..6a849dc 100644 --- a/LastenradBayern/TINK/View/Settings/SettingsPage.xaml.cs +++ b/LastenradBayern/TINK/View/Settings/SettingsPage.xaml.cs @@ -1,4 +1,4 @@ -using TINK.ViewModel; +using TINK.ViewModel; using Xamarin.Forms; using Xamarin.Forms.Xaml; using System.Threading.Tasks; @@ -150,7 +150,7 @@ namespace TINK.View.Settings /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif #if USERFEEDBACKDLG_TRYOUT @@ -165,4 +165,4 @@ namespace TINK.View.Settings } #endif } -} \ No newline at end of file +} diff --git a/LastenradBayern/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs b/LastenradBayern/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs index 3c68bdb..c4b2d62 100644 --- a/LastenradBayern/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs +++ b/LastenradBayern/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs @@ -77,7 +77,7 @@ namespace TINK.View.WhatsNew.Agb #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } } diff --git a/LastenradBayern/TINK/View/WhatsNew/WhatsNewPage.xaml.cs b/LastenradBayern/TINK/View/WhatsNew/WhatsNewPage.xaml.cs index c1c1a2c..9f570c4 100644 --- a/LastenradBayern/TINK/View/WhatsNew/WhatsNewPage.xaml.cs +++ b/LastenradBayern/TINK/View/WhatsNew/WhatsNewPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; using TINK.Model.Device; @@ -80,7 +80,7 @@ namespace TINK.View.WhatsNew #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif /// @@ -103,4 +103,4 @@ namespace TINK.View.WhatsNew }); } } -} \ No newline at end of file +} diff --git a/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml b/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml index 5a2c95e..c89fa55 100644 --- a/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml +++ b/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + diff --git a/Meinkonrad/TINK.iOS/Info.plist b/Meinkonrad/TINK.iOS/Info.plist index 0b58fa3..b7f94eb 100644 --- a/Meinkonrad/TINK.iOS/Info.plist +++ b/Meinkonrad/TINK.iOS/Info.plist @@ -56,8 +56,8 @@ CFBundleDisplayName Mein konrad CFBundleVersion - 370 + 371 CFBundleShortVersionString - 3.0.370 + 3.0.371 diff --git a/Meinkonrad/TINK.iOS/Resources/tink2.png b/Meinkonrad/TINK.iOS/Resources/tink2.png deleted file mode 100644 index b33b28b..0000000 Binary files a/Meinkonrad/TINK.iOS/Resources/tink2.png and /dev/null differ diff --git a/Meinkonrad/TINK/App.xaml b/Meinkonrad/TINK/App.xaml index 0b3695c..ceb8b49 100644 --- a/Meinkonrad/TINK/App.xaml +++ b/Meinkonrad/TINK/App.xaml @@ -14,14 +14,12 @@ - - - - - - - - + + + + + + diff --git a/Meinkonrad/TINK/Images/Konrad.png b/Meinkonrad/TINK/Images/Konrad.png deleted file mode 100644 index 4b5658a..0000000 Binary files a/Meinkonrad/TINK/Images/Konrad.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/Tink2.png b/Meinkonrad/TINK/Images/Tink2.png deleted file mode 100644 index 0388177..0000000 Binary files a/Meinkonrad/TINK/Images/Tink2.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/belt1_image.4XWCQY_679_382.png b/Meinkonrad/TINK/Images/belt1_image.4XWCQY_679_382.png deleted file mode 100644 index 6433405..0000000 Binary files a/Meinkonrad/TINK/Images/belt1_image.4XWCQY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/belt1_image.4XWCQY_793_446.png b/Meinkonrad/TINK/Images/belt1_image.4XWCQY_793_446.png deleted file mode 100644 index 0b6827f..0000000 Binary files a/Meinkonrad/TINK/Images/belt1_image.4XWCQY_793_446.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/belt2_image.X3F1PY_1280_720.png b/Meinkonrad/TINK/Images/belt2_image.X3F1PY_1280_720.png deleted file mode 100644 index 5713063..0000000 Binary files a/Meinkonrad/TINK/Images/belt2_image.X3F1PY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/belt2_image.X3F1PY_679_382.png b/Meinkonrad/TINK/Images/belt2_image.X3F1PY_679_382.png deleted file mode 100644 index 186c8ff..0000000 Binary files a/Meinkonrad/TINK/Images/belt2_image.X3F1PY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/belt3_image.DYOXPY_1280_720.png b/Meinkonrad/TINK/Images/belt3_image.DYOXPY_1280_720.png deleted file mode 100644 index 220e362..0000000 Binary files a/Meinkonrad/TINK/Images/belt3_image.DYOXPY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/belt3_image.DYOXPY_679_382.png b/Meinkonrad/TINK/Images/belt3_image.DYOXPY_679_382.png deleted file mode 100644 index c92b7af..0000000 Binary files a/Meinkonrad/TINK/Images/belt3_image.DYOXPY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/seat1_image.ZQ65PY_680_382.png b/Meinkonrad/TINK/Images/seat1_image.ZQ65PY_680_382.png deleted file mode 100644 index efb9c09..0000000 Binary files a/Meinkonrad/TINK/Images/seat1_image.ZQ65PY_680_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/seat1_image.ZQ65PY_797_448.png b/Meinkonrad/TINK/Images/seat1_image.ZQ65PY_797_448.png deleted file mode 100644 index 8e74cf7..0000000 Binary files a/Meinkonrad/TINK/Images/seat1_image.ZQ65PY_797_448.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/seat2_image.QQZCQY_1280_720.png b/Meinkonrad/TINK/Images/seat2_image.QQZCQY_1280_720.png deleted file mode 100644 index 3b82fc6..0000000 Binary files a/Meinkonrad/TINK/Images/seat2_image.QQZCQY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/seat2_image.QQZCQY_679_382.png b/Meinkonrad/TINK/Images/seat2_image.QQZCQY_679_382.png deleted file mode 100644 index 689de9d..0000000 Binary files a/Meinkonrad/TINK/Images/seat2_image.QQZCQY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/seat3_image.NQ5FQY_1280_720.png b/Meinkonrad/TINK/Images/seat3_image.NQ5FQY_1280_720.png deleted file mode 100644 index 4eb4530..0000000 Binary files a/Meinkonrad/TINK/Images/seat3_image.NQ5FQY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/seat3_image.NQ5FQY_679_382.png b/Meinkonrad/TINK/Images/seat3_image.NQ5FQY_679_382.png deleted file mode 100644 index 699e3b8..0000000 Binary files a/Meinkonrad/TINK/Images/seat3_image.NQ5FQY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_brake1_image.HZ17PY_678_382.png b/Meinkonrad/TINK/Images/trike_brake1_image.HZ17PY_678_382.png deleted file mode 100644 index a6e112a..0000000 Binary files a/Meinkonrad/TINK/Images/trike_brake1_image.HZ17PY_678_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_brake2_image.1YBAQY_1280_720.png b/Meinkonrad/TINK/Images/trike_brake2_image.1YBAQY_1280_720.png deleted file mode 100644 index c68c373..0000000 Binary files a/Meinkonrad/TINK/Images/trike_brake2_image.1YBAQY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_brake2_image.1YBAQY_679_382.png b/Meinkonrad/TINK/Images/trike_brake2_image.1YBAQY_679_382.png deleted file mode 100644 index c389545..0000000 Binary files a/Meinkonrad/TINK/Images/trike_brake2_image.1YBAQY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_brake3_image.FJM2PY_1280_720.png b/Meinkonrad/TINK/Images/trike_brake3_image.FJM2PY_1280_720.png deleted file mode 100644 index 7c68a90..0000000 Binary files a/Meinkonrad/TINK/Images/trike_brake3_image.FJM2PY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_brake3_image.FJM2PY_679_382.png b/Meinkonrad/TINK/Images/trike_brake3_image.FJM2PY_679_382.png deleted file mode 100644 index 3e6bace..0000000 Binary files a/Meinkonrad/TINK/Images/trike_brake3_image.FJM2PY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_stand1_image.4HJ5PY_679_382.png b/Meinkonrad/TINK/Images/trike_stand1_image.4HJ5PY_679_382.png deleted file mode 100644 index dc648b2..0000000 Binary files a/Meinkonrad/TINK/Images/trike_stand1_image.4HJ5PY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_stand1_image.4HJ5PY_800_450.png b/Meinkonrad/TINK/Images/trike_stand1_image.4HJ5PY_800_450.png deleted file mode 100644 index f2ac458..0000000 Binary files a/Meinkonrad/TINK/Images/trike_stand1_image.4HJ5PY_800_450.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_stand2_image.RIX2PY_1280_720.png b/Meinkonrad/TINK/Images/trike_stand2_image.RIX2PY_1280_720.png deleted file mode 100644 index 5a8041f..0000000 Binary files a/Meinkonrad/TINK/Images/trike_stand2_image.RIX2PY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_stand2_image.RIX2PY_679_382.png b/Meinkonrad/TINK/Images/trike_stand2_image.RIX2PY_679_382.png deleted file mode 100644 index d8c493b..0000000 Binary files a/Meinkonrad/TINK/Images/trike_stand2_image.RIX2PY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_stand3_image.FDR7PY_1280_720.png b/Meinkonrad/TINK/Images/trike_stand3_image.FDR7PY_1280_720.png deleted file mode 100644 index b47d5f1..0000000 Binary files a/Meinkonrad/TINK/Images/trike_stand3_image.FDR7PY_1280_720.png and /dev/null differ diff --git a/Meinkonrad/TINK/Images/trike_stand3_image.FDR7PY_679_382.png b/Meinkonrad/TINK/Images/trike_stand3_image.FDR7PY_679_382.png deleted file mode 100644 index 5f769d5..0000000 Binary files a/Meinkonrad/TINK/Images/trike_stand3_image.FDR7PY_679_382.png and /dev/null differ diff --git a/Meinkonrad/TINK/View/Account/AccountPage.xaml.cs b/Meinkonrad/TINK/View/Account/AccountPage.xaml.cs index d470451..273a178 100644 --- a/Meinkonrad/TINK/View/Account/AccountPage.xaml.cs +++ b/Meinkonrad/TINK/View/Account/AccountPage.xaml.cs @@ -1,4 +1,4 @@ -using TINK.ViewModel; +using TINK.ViewModel; using Xamarin.Forms; using Xamarin.Forms.Xaml; using System.Threading.Tasks; @@ -150,7 +150,7 @@ namespace TINK.View.Account #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } -} \ No newline at end of file +} diff --git a/Meinkonrad/TINK/View/Bike/ILockItBike.xaml b/Meinkonrad/TINK/View/Bike/ILockItBike.xaml index 74e022e..b759204 100644 --- a/Meinkonrad/TINK/View/Bike/ILockItBike.xaml +++ b/Meinkonrad/TINK/View/Bike/ILockItBike.xaml @@ -11,8 +11,13 @@ - - + + + + + + + + VerticalOptions="End"> - - - - - - - - - - - - - diff --git a/Meinkonrad/TINK/View/Settings/SettingsPage.xaml.cs b/Meinkonrad/TINK/View/Settings/SettingsPage.xaml.cs index 00d3271..6a849dc 100644 --- a/Meinkonrad/TINK/View/Settings/SettingsPage.xaml.cs +++ b/Meinkonrad/TINK/View/Settings/SettingsPage.xaml.cs @@ -1,4 +1,4 @@ -using TINK.ViewModel; +using TINK.ViewModel; using Xamarin.Forms; using Xamarin.Forms.Xaml; using System.Threading.Tasks; @@ -150,7 +150,7 @@ namespace TINK.View.Settings /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif #if USERFEEDBACKDLG_TRYOUT @@ -165,4 +165,4 @@ namespace TINK.View.Settings } #endif } -} \ No newline at end of file +} diff --git a/Meinkonrad/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs b/Meinkonrad/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs index 17a0d1a..195c6a1 100644 --- a/Meinkonrad/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs +++ b/Meinkonrad/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs @@ -77,7 +77,7 @@ namespace TINK.View.WhatsNew.Agb #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } } diff --git a/Meinkonrad/TINK/View/WhatsNew/WhatsNewPage.xaml.cs b/Meinkonrad/TINK/View/WhatsNew/WhatsNewPage.xaml.cs index c1c1a2c..9f570c4 100644 --- a/Meinkonrad/TINK/View/WhatsNew/WhatsNewPage.xaml.cs +++ b/Meinkonrad/TINK/View/WhatsNew/WhatsNewPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; using TINK.Model.Device; @@ -80,7 +80,7 @@ namespace TINK.View.WhatsNew #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif /// @@ -103,4 +103,4 @@ namespace TINK.View.WhatsNew }); } } -} \ No newline at end of file +} diff --git a/ShareeSharedGuiLib/View/BarLevelView.xaml.cs b/ShareeSharedGuiLib/View/BarLevelView.xaml.cs index 69f663e..1d9395f 100644 --- a/ShareeSharedGuiLib/View/BarLevelView.xaml.cs +++ b/ShareeSharedGuiLib/View/BarLevelView.xaml.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using ShareeSharedGuiLib.ViewModel; using Xamarin.Forms; using Xamarin.Forms.Xaml; @@ -24,7 +19,7 @@ namespace ShareeSharedGuiLib.View } /// - /// Create bindable property to to allow "Maximum"- proptery to act as a valid target for data binding. + /// Create bindable property to allow "Maximum"- property to act as a valid target for data binding. /// public static readonly BindableProperty MaximumProperty = BindableProperty.Create( "Maximum", @@ -34,7 +29,7 @@ namespace ShareeSharedGuiLib.View propertyChanged: OnMaximumChanged); /// - /// Holds the count of bars wich represent charing level full. + /// Holds the count of bars which represent charing level full. /// public string Maximum { @@ -58,7 +53,7 @@ namespace ShareeSharedGuiLib.View /// - /// Create bindable property to to allow "Current"- proptery to act as a valid target for data binding. + /// Create bindable property to allow "Current"- property to act as a valid target for data binding. /// public static readonly BindableProperty CurrentProperty = BindableProperty.Create( "Current", @@ -68,7 +63,7 @@ namespace ShareeSharedGuiLib.View propertyChanged: OnCurrentChanged); /// - /// Holds the count of bars wich represent the current charing level. + /// Holds the count of bars which represent the current charing level. /// public string Current { @@ -91,4 +86,4 @@ namespace ShareeSharedGuiLib.View public void SetCurrent(int? current) => _LocalViewModel.Current = current; } -} \ No newline at end of file +} diff --git a/TINK/TINK.Android/Properties/AndroidManifest.xml b/TINK/TINK.Android/Properties/AndroidManifest.xml index b11e566..d8c4077 100644 --- a/TINK/TINK.Android/Properties/AndroidManifest.xml +++ b/TINK/TINK.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + diff --git a/TINK/TINK.iOS/Info.plist b/TINK/TINK.iOS/Info.plist index 435b23f..c4a8ce3 100644 --- a/TINK/TINK.iOS/Info.plist +++ b/TINK/TINK.iOS/Info.plist @@ -56,8 +56,8 @@ CFBundleDisplayName sharee.bike CFBundleVersion - 370 + 371 CFBundleShortVersionString - 3.0.370 + 3.0.371 diff --git a/TINK/TINK.iOS/Resources/Font Awesome 5 Free-Solid-900.otf b/TINK/TINK.iOS/Resources/Font Awesome 5 Free-Solid-900.otf deleted file mode 100644 index 8fb28d5..0000000 Binary files a/TINK/TINK.iOS/Resources/Font Awesome 5 Free-Solid-900.otf and /dev/null differ diff --git a/TINK/TINK/App.xaml b/TINK/TINK/App.xaml index e242651..6014a07 100644 --- a/TINK/TINK/App.xaml +++ b/TINK/TINK/App.xaml @@ -15,10 +15,11 @@ - + + diff --git a/TINK/TINK/View/Account/AccountPage.xaml.cs b/TINK/TINK/View/Account/AccountPage.xaml.cs index d470451..273a178 100644 --- a/TINK/TINK/View/Account/AccountPage.xaml.cs +++ b/TINK/TINK/View/Account/AccountPage.xaml.cs @@ -1,4 +1,4 @@ -using TINK.ViewModel; +using TINK.ViewModel; using Xamarin.Forms; using Xamarin.Forms.Xaml; using System.Threading.Tasks; @@ -150,7 +150,7 @@ namespace TINK.View.Account #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } -} \ No newline at end of file +} diff --git a/TINK/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs b/TINK/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs index 0e47316..3f5a7d7 100644 --- a/TINK/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs +++ b/TINK/TINK/View/BikesAtStation/BikesAtStationPage.xaml.cs @@ -250,7 +250,7 @@ namespace TINK.View.BikesAtStation /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } } diff --git a/TINK/TINK/View/Contact/ContactPage.xaml.cs b/TINK/TINK/View/Contact/ContactPage.xaml.cs index 299b56e..b091b3a 100644 --- a/TINK/TINK/View/Contact/ContactPage.xaml.cs +++ b/TINK/TINK/View/Contact/ContactPage.xaml.cs @@ -1,4 +1,4 @@ -using Serilog; +using Serilog; using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; @@ -29,8 +29,11 @@ namespace TINK.View.Contact { InitializeComponent(); + var l_oModel = App.ModelRoot; + ViewModel = new ContactPageViewModel( App.ModelRoot.Flavor.GetDisplayName(), + l_oModel, () => App.CreateAttachment(), () => DependencyService.Get().OpenUrl(DependencyService.Get().StoreUrl), this); @@ -122,7 +125,7 @@ namespace TINK.View.Contact #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif #if USEFLYOUT @@ -133,4 +136,4 @@ namespace TINK.View.Contact public INavigationMasterDetail NavigationMasterDetail { set; private get; } #endif } -} \ No newline at end of file +} diff --git a/TINK/TINK/View/Contact/SelectStationPage.xaml.cs b/TINK/TINK/View/Contact/SelectStationPage.xaml.cs index d1f97d4..25d551c 100644 --- a/TINK/TINK/View/Contact/SelectStationPage.xaml.cs +++ b/TINK/TINK/View/Contact/SelectStationPage.xaml.cs @@ -117,7 +117,7 @@ namespace TINK.View.Contact #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif #if USEFLYOUT diff --git a/TINK/TINK/View/FeedbackPopup.xaml.cs b/TINK/TINK/View/FeedbackPopup.xaml.cs index f4b5fe0..38825db 100644 --- a/TINK/TINK/View/FeedbackPopup.xaml.cs +++ b/TINK/TINK/View/FeedbackPopup.xaml.cs @@ -12,7 +12,7 @@ namespace TINK.View /// Object holding info about battery. For some batteries charging level might need to be updated by user. /// Co2 saving information. public FeedbackPopup( - IBattery battery = null, + IBatteryMutable battery = null, string co2Saving = null) { InitializeComponent(); diff --git a/TINK/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml b/TINK/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml index a4aff29..77f2628 100644 --- a/TINK/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml +++ b/TINK/TINK/View/FeesAndBikes/FeesAndBikesPage.xaml @@ -1,29 +1,88 @@ - + + + + + + + + - - - + + + + + + + + + + + + + + - - - - - - + + + + + + - + + + + - - - - - + + - + App.ModelRoot.GetConnector(App.ModelRoot.GetIsConnected()).Query, resourceUrls => App.ModelRoot.ResourceUrls = resourceUrls); diff --git a/TINK/TINK/View/FindBike/FindBikePage.xaml.cs b/TINK/TINK/View/FindBike/FindBikePage.xaml.cs index f8ccfa2..bbe8954 100644 --- a/TINK/TINK/View/FindBike/FindBikePage.xaml.cs +++ b/TINK/TINK/View/FindBike/FindBikePage.xaml.cs @@ -182,7 +182,7 @@ namespace TINK.View.FindBike /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } } diff --git a/TINK/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs b/TINK/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs index 9af80a4..d20bf33 100644 --- a/TINK/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs +++ b/TINK/TINK/View/Info/BikeInfo/BikeInfoCarouselPage.xaml.cs @@ -123,7 +123,7 @@ namespace TINK.View.Info.BikeInfo #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } } diff --git a/TINK/TINK/View/Info/InfoTabbedPage.xaml b/TINK/TINK/View/Info/InfoTabbedPage.xaml index a0c2135..68283eb 100644 --- a/TINK/TINK/View/Info/InfoTabbedPage.xaml +++ b/TINK/TINK/View/Info/InfoTabbedPage.xaml @@ -3,104 +3,162 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TINK.View.Info.InfoPage" xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib" + Style="{StaticResource TabbedPageStyle}" x:Name="TabbedInfoPage"> - + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + - - + + diff --git a/TINK/TINK/View/Login/LoginPage.xaml.cs b/TINK/TINK/View/Login/LoginPage.xaml.cs index 95785d6..c00656e 100644 --- a/TINK/TINK/View/Login/LoginPage.xaml.cs +++ b/TINK/TINK/View/Login/LoginPage.xaml.cs @@ -121,7 +121,7 @@ namespace TINK.View.Login #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } } diff --git a/TINK/TINK/View/Map/MapPage.xaml b/TINK/TINK/View/Map/MapPage.xaml index eb1f5e4..a9e21c7 100644 --- a/TINK/TINK/View/Map/MapPage.xaml +++ b/TINK/TINK/View/Map/MapPage.xaml @@ -126,12 +126,12 @@ + Margin="-3"> DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif #if USEFLYOUT diff --git a/TINK/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs b/TINK/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs index 872e5bc..d967724 100644 --- a/TINK/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs +++ b/TINK/TINK/View/MiniSurvey/MiniSurveyPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; using TINK.ViewModel.MiniSurvey; @@ -93,7 +93,7 @@ namespace TINK.View.MiniSurvey /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } -} \ No newline at end of file +} diff --git a/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs b/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs index fca7a06..4bc4681 100644 --- a/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs +++ b/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs @@ -181,7 +181,7 @@ namespace TINK.View.MyBikes /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif } } diff --git a/TINK/TINK/View/RootShell/AppShell.xaml b/TINK/TINK/View/RootShell/AppShell.xaml index 2f27f03..8acae5a 100644 --- a/TINK/TINK/View/RootShell/AppShell.xaml +++ b/TINK/TINK/View/RootShell/AppShell.xaml @@ -70,6 +70,16 @@ + + + + + + + + - - - - - - - - + @@ -103,7 +104,7 @@ Title="{Binding TabbedPageIngoTitle}" ContentTemplate="{DataTemplate info:InfoPage}"> - + diff --git a/TINK/TINK/View/Settings/SettingsPage.xaml.cs b/TINK/TINK/View/Settings/SettingsPage.xaml.cs index 00d3271..6a849dc 100644 --- a/TINK/TINK/View/Settings/SettingsPage.xaml.cs +++ b/TINK/TINK/View/Settings/SettingsPage.xaml.cs @@ -1,4 +1,4 @@ -using TINK.ViewModel; +using TINK.ViewModel; using Xamarin.Forms; using Xamarin.Forms.Xaml; using System.Threading.Tasks; @@ -150,7 +150,7 @@ namespace TINK.View.Settings /// Displays user feedback popup. /// Co2 saving information. /// User feedback. - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => await Navigation.ShowPopupAsync(new FeedbackPopup(battery, co2Saving)); #endif #if USERFEEDBACKDLG_TRYOUT @@ -165,4 +165,4 @@ namespace TINK.View.Settings } #endif } -} \ No newline at end of file +} diff --git a/TINK/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs b/TINK/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs index 3c68bdb..c4b2d62 100644 --- a/TINK/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs +++ b/TINK/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs @@ -77,7 +77,7 @@ namespace TINK.View.WhatsNew.Agb #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif } } diff --git a/TINK/TINK/View/WhatsNew/WhatsNewPage.xaml.cs b/TINK/TINK/View/WhatsNew/WhatsNewPage.xaml.cs index c1c1a2c..9f570c4 100644 --- a/TINK/TINK/View/WhatsNew/WhatsNewPage.xaml.cs +++ b/TINK/TINK/View/WhatsNew/WhatsNewPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; using TINK.Model.Device; @@ -80,7 +80,7 @@ namespace TINK.View.WhatsNew #if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); #else - public async Task DisplayUserFeedbackPopup(IBattery battery = null, string co2Saving = null) => throw new NotSupportedException(); + public async Task DisplayUserFeedbackPopup(IBatteryMutable battery = null, string co2Saving = null) => throw new NotSupportedException(); #endif /// @@ -103,4 +103,4 @@ namespace TINK.View.WhatsNew }); } } -} \ No newline at end of file +} diff --git a/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfo.cs b/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfo.cs index 1a2fe18..b3aa42a 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfo.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfo.cs @@ -22,7 +22,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC /// /// Holds the drive object. /// - public Drive Drive { get; } + public DriveMutable Drive { get; } /// Gets the information where the data origins from. public DataSource DataSource { get; } @@ -32,7 +32,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC protected BikeInfo( IStateInfo stateInfo, Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, bool? isDemo = DEFAULTVALUEISDEMO, IEnumerable group = null, diff --git a/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfoMutable.cs b/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfoMutable.cs index e1e0608..72a7949 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfoMutable.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/BC/BikeInfoMutable.cs @@ -15,7 +15,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC private readonly Bike _Bike; /// Holds the drive of the bike. - private readonly Drive _Drive; + private readonly DriveMutable _Drive; /// Holds the state info of the bike. private readonly StateInfoMutable _StateInfo; @@ -31,7 +31,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC /// Bike state info. protected BikeInfoMutable( Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, bool isDemo = BikeInfo.DEFAULTVALUEISDEMO, IEnumerable group = null, @@ -105,7 +105,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC public string Description => _Bike.Description; - public Drive Drive => _Drive; + public DriveMutable Drive => _Drive; /// /// Fired whenever property of bike changes. diff --git a/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfo.cs b/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfo.cs index 49a8109..2744263 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfo.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfo.cs @@ -18,7 +18,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC /// /// Holds the drive. /// - Drive Drive { get; } + DriveMutable Drive { get; } /// Gets or sets the information where the data origins from. DataSource DataSource { get; } diff --git a/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfoMutable.cs b/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfoMutable.cs index fe47d4b..9a1dc1f 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfoMutable.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/BC/IBikeInfoMutable.cs @@ -59,7 +59,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC /// /// Hold the drive object. /// - Drive Drive { get; } + DriveMutable Drive { get; } /// Gets or sets the information where the data origins from. DataSource DataSource { get; set; } diff --git a/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/BikeInfo.cs b/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/BikeInfo.cs index 4fe6901..267b3e6 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/BikeInfo.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/BikeInfo.cs @@ -20,7 +20,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock /// Hold tariff description of bike. public BikeInfo( Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, int lockId, Guid lockGuid, @@ -65,7 +65,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock /// Date time provider to calculate remaining time. public BikeInfo( Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, int lockId, Guid lockGuid, @@ -121,7 +121,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock /// public BikeInfo( Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, int lockId, Guid lockGuid, diff --git a/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/CloseCommand.cs b/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/CloseCommand.cs index c014335..647c069 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/CloseCommand.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/CloseCommand.cs @@ -32,7 +32,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command } /// - /// Possible steps of closing a lock. + /// Possible states of closing a lock. /// public enum State { @@ -182,7 +182,8 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command } } - // Start query geolocation data. + //// Start Action + //// Step: Start query geolocation data. Log.ForContext().Debug($"Starting step {Step.StartingQueryingLocation}..."); InvokeCurrentStep(Step.StartingQueryingLocation); var ctsLocation = new CancellationTokenSource(); @@ -199,7 +200,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command await InvokeCurrentStateAsync(State.StartGeolocationException, ex.Message); } - // Close lock. + //// Step: Close lock. IGeolocation currentLocation; Log.ForContext().Debug($"Starting step {Step.ClosingLock}..."); InvokeCurrentStep(Step.ClosingLock); @@ -236,7 +237,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command // Signal cts to cancel getting geolocation. ctsLocation.Cancel(); - //// Step: Wait until getting geolocation and stop polling has completed. + // Wait until getting geolocation and stop polling has completed. currentLocation = await WaitForPendingTasks(currentLocationTask); // Update current state from exception @@ -256,14 +257,14 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command throw; } - // Step: Update backend. + // Update backend. // Do this even if current lock state is open (lock state must not necessarily be open before try to open, i.e. something undefined between open and closed). await UpdateLockingState(currentLocation, timeStampNow); throw; } - //// Step: Wait until getting geolocation and stop polling has completed. + //// Step: Wait until getting geolocation and stop polling has completed. currentLocation = await WaitForPendingTasks(currentLocationTask); bike.LockInfo.State = lockingState?.GetLockingState() ?? LockingState.UnknownDisconnected; @@ -276,7 +277,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command return; } - //// Step: Update backend. + //// Step: Update backend. await UpdateLockingState(currentLocation, timeStampNow); } } diff --git a/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/GetLockedLocationCommand.cs b/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/GetLockedLocationCommand.cs index 60f8335..b5edb17 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/GetLockedLocationCommand.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/GetLockedLocationCommand.cs @@ -97,6 +97,8 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command } } + //// Start Action + //// Step: Start query geolocation data. InvokeCurrentStep(Step.StartingQueryLocation); // Get geolocation which was requested when closing lock. @@ -123,7 +125,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command await InvokeCurrentStateAsync(State.DisconnetedNoLocationError, ""); - // Disconnect lock. + //// Step: Disconnect lock. InvokeCurrentStep(Step.DisconnectingLockOnDisconnectedNoLocationError); try { diff --git a/TINKLib/Model/Bikes/BikeInfoNS/CopriLock/BikeInfo.cs b/TINKLib/Model/Bikes/BikeInfoNS/CopriLock/BikeInfo.cs index aa3c9a0..b07e38a 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/CopriLock/BikeInfo.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/CopriLock/BikeInfo.cs @@ -22,7 +22,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock /// Hold tariff description of bike. public BikeInfo( Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, string currentStationId, LockInfo lockInfo, @@ -70,7 +70,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock /// Provider for current date time to calculate remaining time on demand for state of type reserved. public BikeInfo( Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, DateTime requestedAt, string mailAddress, @@ -122,7 +122,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock /// Hold tariff description of bike. public BikeInfo( Bike bike, - Drive drive, + DriveMutable drive, DataSource dataSource, DateTime bookedAt, string mailAddress, diff --git a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/Battery.cs b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/Battery.cs index 0734581..5825985 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/Battery.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/Battery.cs @@ -2,32 +2,35 @@ using Serilog; namespace TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS { + /// + /// Holds the state of a chargeable battery. + /// public class Battery : IBattery { private Battery() { } /// - /// Holds the current charging level of the battery in percent, double.NaN if unknown. + /// Gets the current charging level of the battery in percent, double.NaN if unknown. /// public double CurrentChargePercent { get; private set; } = double.NaN; /// - /// Holds the current charging level of the battery in bars, null if unknown. + /// Gets the current charging level of the battery in bars, null if unknown. /// public int? CurrentChargeBars { get; private set; } = null; /// - /// Holds the maximum charging level of the battery in bars, null if unknown. + /// Gets the maximum charging level of the battery in bars, null if unknown. /// public int? MaxChargeBars { get; private set; } = null; /// - /// Holds whether backend is aware of battery charging level. + /// Gets whether backend is aware of battery charging level. /// public bool? IsBackendAccessible { get; private set; } = null; /// - /// Holds whether to display battery level or not. + /// Gets whether to display battery level or not. /// public bool? IsHidden { get; private set; } = null; diff --git a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/BatteryMutable.cs b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/BatteryMutable.cs new file mode 100644 index 0000000..b0e8f5b --- /dev/null +++ b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/BatteryMutable.cs @@ -0,0 +1,84 @@ +using System.ComponentModel; + +namespace TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS +{ + /// + /// Manages the state of a chargeable battery. + /// + public class BatteryMutable : IBatteryMutable, INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + IBattery _battery; + + public BatteryMutable(IBattery battery) + { + _battery = battery; + } + + /// + /// Gets the current charging level of the battery in percent, double.NaN if unknown. + /// + public double CurrentChargePercent => _battery.CurrentChargePercent; + + /// + /// Gets or sets the current charging level of the battery in bars, null if unknown. + /// + public int? CurrentChargeBars + { + get => _battery.CurrentChargeBars; + set + { + double GetCurrentChargePercent() + { + if (value == null) + { + // Filling level is unknown. + return double.NaN; + } + + if (_battery.MaxChargeBars == null || _battery.MaxChargeBars == 0) + { + // Percentage filling level can not be calculated. + return _battery.CurrentChargePercent; + } + + return (int)(100 * value / _battery.MaxChargeBars); + } + + if (_battery.CurrentChargeBars == value) + { + // Nothing to do. + return; + } + + _battery = new Battery.Builder + { + MaxChargeBars = _battery.MaxChargeBars, + IsBackendAccessible = _battery.IsBackendAccessible, + IsHidden = _battery.IsHidden, + CurrentChargeBars = value, + CurrentChargePercent = GetCurrentChargePercent(), + }.Build(); + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentChargeBars))); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentChargePercent))); + } + } + + /// + /// Gets the maximum charging level of the battery in bars, null if unknown. + /// + public int? MaxChargeBars => _battery.MaxChargeBars; + + /// + /// Gets whether backend is aware of battery charging level. + /// + public bool? IsBackendAccessible => _battery.IsBackendAccessible; + + /// + /// Gets whether to display battery level or not. + /// + public bool? IsHidden => _battery.IsHidden; + } +} diff --git a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/IBattery.cs b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/IBattery.cs index 1ee330d..1078e3c 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/IBattery.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/IBattery.cs @@ -1,7 +1,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS { - public interface IBattery + public interface IBattery { /// /// Holds the current charging level of the battery in percent, double.NaN if unknown. diff --git a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/IBatteryMutable.cs b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/IBatteryMutable.cs new file mode 100644 index 0000000..3452516 --- /dev/null +++ b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/IBatteryMutable.cs @@ -0,0 +1,35 @@ +using System.ComponentModel; + +namespace TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS +{ + /// + /// Manages the state of a chargeable battery. + /// + public interface IBatteryMutable : INotifyPropertyChanged + { + /// + /// Gets the current charging level of the battery in percent, double.NaN if unknown. + /// + double CurrentChargePercent { get; } + + /// + /// Gets or sets the current charging level of the battery in bars. Must not be larger than MaxChargeBars, null if unknown. + /// + int? CurrentChargeBars { get; set; } + + /// + /// Gets the maximum charging level of the battery in bars, null if unknown. + /// + int? MaxChargeBars { get; } + + /// + /// Gets whether backend is aware of battery charging level. + /// + bool? IsBackendAccessible { get; } + + /// + /// Gets whether to display battery level or not. + /// + bool? IsHidden { get; } + } +} diff --git a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/Drive.cs b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/DriveMutable.cs similarity index 58% rename from TINKLib/Model/Bikes/BikeInfoNS/DriveNS/Drive.cs rename to TINKLib/Model/Bikes/BikeInfoNS/DriveNS/DriveMutable.cs index fc6962e..715ee3f 100644 --- a/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/Drive.cs +++ b/TINKLib/Model/Bikes/BikeInfoNS/DriveNS/DriveMutable.cs @@ -1,4 +1,4 @@ -using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; +using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; using TINK.Model.Bikes.BikeInfoNS.DriveNS.EngineNS; namespace TINK.Model.Bikes.BikeInfoNS.DriveNS @@ -6,32 +6,32 @@ namespace TINK.Model.Bikes.BikeInfoNS.DriveNS public enum DriveType { /// - /// Bike without pedalling aid. + /// Bike without pedaling aid. /// SoleHumanPowered, /// - /// pedal electric cycle: Pedalling is assisted by an electric engine. + /// pedal electric cycle: Pedaling is assisted by an electric engine. /// Pedelec } - public class Drive + public class DriveMutable { - public Drive( + public DriveMutable( IEngine engine = null, IBattery battery = null) { if (engine == null) { Engine = new Engine(); - Battery = new Battery.Builder().Build(); + Battery = new BatteryMutable(new Battery.Builder().Build()); Type = DriveType.SoleHumanPowered; return; } Engine = engine; - Battery = battery ?? new Battery.Builder().Build(); + Battery = new BatteryMutable(battery ?? new Battery.Builder().Build()); Type = DriveType.Pedelec; } @@ -48,15 +48,6 @@ namespace TINK.Model.Bikes.BikeInfoNS.DriveNS /// /// Battery powering the engine. /// - public IBattery _Battery = new Battery.Builder().Build(); - - /// - /// Battery powering the engine. - /// - public IBattery Battery - { - get => _Battery; - set => _Battery = value ?? new Battery.Builder().Build(); - } + public IBatteryMutable Battery { get; private set; } } } diff --git a/TINKLib/Model/Connector/Updater/DriveFactory.cs b/TINKLib/Model/Connector/Updater/DriveFactory.cs index 0d5b900..81859b1 100644 --- a/TINKLib/Model/Connector/Updater/DriveFactory.cs +++ b/TINKLib/Model/Connector/Updater/DriveFactory.cs @@ -7,16 +7,16 @@ namespace TINK.Model.Connector.Updater { public static class DriveFactory { - public static Drive Create(this BikeType bikeType) + public static DriveMutable Create(this BikeType bikeType) { if (string.IsNullOrEmpty(bikeType?.engine?.manufacturer)) { // Bike is has no engine - return new Drive(); + return new DriveMutable(); } // Bike is a pedelec. - return new Drive( + return new DriveMutable( new Engine(bikeType?.engine?.manufacturer), new Battery.Builder { diff --git a/TINKLib/Model/WhatsNew.cs b/TINKLib/Model/WhatsNew.cs index 54f3a66..48fe518 100644 --- a/TINKLib/Model/WhatsNew.cs +++ b/TINKLib/Model/WhatsNew.cs @@ -712,6 +712,16 @@ namespace TINK.Model new Version(3, 0, 370), AppResources.ChangeLog_3_0_370 }, + { + new Version(3, 0, 371), + AppResources.ChangeLog_3_0_371_SB, + new List { AppFlavor.ShareeBike } + }, + { + new Version(3, 0, 371), + AppResources.ChangeLog_3_0_371_MK, + new List { AppFlavor.MeinKonrad} + }, }; /// Manges the whats new information. diff --git a/TINKLib/MultilingualResources/AppResources.Designer.cs b/TINKLib/MultilingualResources/AppResources.Designer.cs index 12f7890..c0e3657 100644 --- a/TINKLib/MultilingualResources/AppResources.Designer.cs +++ b/TINKLib/MultilingualResources/AppResources.Designer.cs @@ -1452,9 +1452,6 @@ namespace TINK.MultilingualResources { } } - /// - /// Looks up a localized string similar to Changes in the return process: FIRST you must always complete the "Close lock" process. Only THEN you can finish the rental! If your rental was successfully ended, you will see a confirmation dialog.. - /// public static string ChangeLog_3_0_367_MK_SB { get { return ResourceManager.GetString("ChangeLog_3_0_367_MK_SB", resourceCulture); @@ -1475,6 +1472,25 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to 1. Easy access to FAQ: In the menu under Help, you can now easily search for a solution yourself for questions and problems.<br/> + ///2. City area vs. suburb bikes: Rental bikes that belong to the city area and cannot be returned to the suburbs are now marked by appropriate icons. Conversely, rental bikes from the suburbs cannot be returned in the city area (also marked with icons)!. + /// + public static string ChangeLog_3_0_371_MK { + get { + return ResourceManager.GetString("ChangeLog_3_0_371_MK", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Easy access to FAQ: In the menu under Help, you can now easily search for a solution yourself for questions and problems.. + /// + public static string ChangeLog_3_0_371_SB { + get { + return ResourceManager.GetString("ChangeLog_3_0_371_SB", resourceCulture); + } + } + /// /// Looks up a localized string similar to Your session has expired. Please login new and try again.. /// @@ -2109,6 +2125,15 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to City area. + /// + public static string MarkingBikeIsBoundToCity { + get { + return ResourceManager.GetString("MarkingBikeIsBoundToCity", resourceCulture); + } + } + /// /// Looks up a localized string similar to There are currently no bicycles available at this station.. /// @@ -2263,7 +2288,7 @@ namespace TINK.MultilingualResources { } /// - /// Looks up a localized string similar to Instructions. + /// Looks up a localized string similar to Help. /// public static string MarkingFeesAndBikes { get { @@ -2687,7 +2712,7 @@ namespace TINK.MultilingualResources { } /// - /// Looks up a localized string similar to Instructions. + /// Looks up a localized string similar to Manual. /// public static string MarkingTabBikes { get { @@ -2696,7 +2721,16 @@ namespace TINK.MultilingualResources { } /// - /// Looks up a localized string similar to Pricing. + /// Looks up a localized string similar to FAQ. + /// + public static string MarkingTabFaq { + get { + return ResourceManager.GetString("MarkingTabFaq", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tariffs. /// public static string MarkingTabFees { get { @@ -2839,6 +2873,26 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to The rental of this {0} can only be terminated at {0} stations within the city area. + /// + ///A rental end in the suburbs Litzelstetten, Dingelsdorf, Wallhausen and Dettingen is not allowed.. + /// + public static string MessageBikeIsBoundToCityInfoText { + get { + return ResourceManager.GetString("MessageBikeIsBoundToCityInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bike belongs in the city area!. + /// + public static string MessageBikeIsBoundToCityInfoTitle { + get { + return ResourceManager.GetString("MessageBikeIsBoundToCityInfoTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Please enable Bluetooth to manage bike lock/locks.. /// @@ -3131,15 +3185,6 @@ namespace TINK.MultilingualResources { } } - /// - /// Looks up a localized string similar to Information. - /// - public static string MessageTitleInformation { - get { - return ResourceManager.GetString("MessageTitleInformation", resourceCulture); - } - } - /// /// Looks up a localized string similar to Warning. /// @@ -3475,6 +3520,24 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Please state your name and/or the e-mail address of your rental bike account:. + /// + public static string SupportmailBodyNotLoggedIn { + get { + return ResourceManager.GetString("SupportmailBodyNotLoggedIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Describe your problem:. + /// + public static string SupportmailBodyText { + get { + return ResourceManager.GetString("SupportmailBodyText", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0}-app request. /// diff --git a/TINKLib/MultilingualResources/AppResources.de.resx b/TINKLib/MultilingualResources/AppResources.de.resx index c76fb85..50c36e1 100644 --- a/TINKLib/MultilingualResources/AppResources.de.resx +++ b/TINKLib/MultilingualResources/AppResources.de.resx @@ -79,13 +79,13 @@ Meine Räder - Bedienung + Hilfe Einstellungen - Bedienung + Anleitung Tarife @@ -1143,6 +1143,17 @@ Vielen Dank für Ihre Fahrt! {0}-App Anfrage: Station {1} + + Stadtgebiet + + + Die Miete dieses {0}s kann nur an {0}-Stationen innerhalb des Stadtgebiets beendet werden. + +Ein Mietende in den Vororten Litzelstetten, Dingelsdorf, Wallhausen und Dettingen ist nicht erlaubt. + + + Rad gehört ins Stadtgebiet! + Bitte treten Sie so nah wie möglich an das Fahrradschloss heran und versuchen Sie es erneut. @@ -1238,4 +1249,20 @@ Wichtig: Senden Sie eine E-Mail an den Kundensupport (sonst wird Ihre bezahlte M - Miete beenden (vorausgesetzt Sie befinden sich an einer passenden Station).<br/> 3. Nach Wahl von <b>Miete beenden</b>: Prozess verfolgen, Rückmeldung ausfüllen und <b>unbedingt die Mietende-Bestätigung abwarten!</b> + + FAQ + + + Beschreiben Sie Ihr Problem: + + + Bitte nennen Sie Ihren Namen und/oder die E-Mail Adresse Ihres Mietrad-Accounts: + + + 1. Einfacher Zugriff auf die FAQ: Im Menü unter Hilfe können Sie bei Fragen und Problemen nun ganz einfach selber nach einer Lösung suchen.<br/> +2. Stadtgebiet- vs. Vorort-Räder: Mieträder, die ins Stadtgebiet gehören und nicht in den Vororten zurückgegeben werden können, sind nun durch entsprechende Icons markiert. Andersherum gilt, dass Mieträder aus den Vororten nicht im Stadtgebiet zurückgegeben werden können (ebenfalls mit Icons gekennzeichnet)! + + + Einfacher Zugriff auf die FAQ: Im Menü unter Hilfe können Sie bei Fragen und Problemen nun ganz einfach selber nach einer Lösung suchen. + \ No newline at end of file diff --git a/TINKLib/MultilingualResources/AppResources.resx b/TINKLib/MultilingualResources/AppResources.resx index 1be65cc..830e3c4 100644 --- a/TINKLib/MultilingualResources/AppResources.resx +++ b/TINKLib/MultilingualResources/AppResources.resx @@ -187,7 +187,7 @@ Rental can be ended if Contact - Instructions + Help Logged in as {0}. @@ -211,10 +211,10 @@ Rental can be ended if Settings - Instructions + Manual - Pricing + Tariffs OK @@ -1269,6 +1269,17 @@ Thank you for your ride! {0}-app request + + City area + + + The rental of this {0} can only be terminated at {0} stations within the city area. + +A rental end in the suburbs Litzelstetten, Dingelsdorf, Wallhausen and Dettingen is not allowed. + + + Bike belongs in the city area! + Position of lock bolt is unknown. Lock could be closed or open. Please try again or report bike to operator! @@ -1330,4 +1341,20 @@ Important: Send an email to customer support (otherwise your paid rental will co - End rental (provided you are at a suitable station).<br/> 3. After choosing <b>End rental</b>: Follow the process, fill in the feedback and <b>wait for the end of rental confirmation!</b> + + FAQ + + + Describe your problem: + + + Please state your name and/or the e-mail address of your rental bike account: + + + 1. Easy access to FAQ: In the menu under Help, you can now easily search for a solution yourself for questions and problems.<br/> +2. City area vs. suburb bikes: Rental bikes that belong to the city area and cannot be returned to the suburbs are now marked by appropriate icons. Conversely, rental bikes from the suburbs cannot be returned in the city area (also marked with icons)! + + + Easy access to FAQ: In the menu under Help, you can now easily search for a solution yourself for questions and problems. + \ No newline at end of file diff --git a/TINKLib/MultilingualResources/TINKLib.de.xlf b/TINKLib/MultilingualResources/TINKLib.de.xlf index df8fb01..a9b028a 100644 --- a/TINKLib/MultilingualResources/TINKLib.de.xlf +++ b/TINKLib/MultilingualResources/TINKLib.de.xlf @@ -95,19 +95,19 @@ Meine Räder - Instructions - Bedienung + Help + Hilfe Settings Einstellungen - Instructions - Bedienung + Manual + Anleitung - Pricing + Tariffs Tarife @@ -1574,6 +1574,22 @@ Vielen Dank für Ihre Fahrt! {0}-app request: station {1} {0}-App Anfrage: Station {1} + + City area + Stadtgebiet + + + The rental of this {0} can only be terminated at {0} stations within the city area. + +A rental end in the suburbs Litzelstetten, Dingelsdorf, Wallhausen and Dettingen is not allowed. + Die Miete dieses {0}s kann nur an {0}-Stationen innerhalb des Stadtgebiets beendet werden. + +Ein Mietende in den Vororten Litzelstetten, Dingelsdorf, Wallhausen und Dettingen ist nicht erlaubt. + + + Bike belongs in the city area! + Rad gehört ins Stadtgebiet! + Please step as close as possible to the bike lock and try again. Bitte treten Sie so nah wie möglich an das Fahrradschloss heran und versuchen Sie es erneut. @@ -1710,6 +1726,28 @@ Wichtig: Senden Sie eine E-Mail an den Kundensupport (sonst wird Ihre bezahlte M - Miete beenden (vorausgesetzt Sie befinden sich an einer passenden Station).<br/> 3. Nach Wahl von <b>Miete beenden</b>: Prozess verfolgen, Rückmeldung ausfüllen und <b>unbedingt die Mietende-Bestätigung abwarten!</b> + + FAQ + FAQ + + + Describe your problem: + Beschreiben Sie Ihr Problem: + + + Please state your name and/or the e-mail address of your rental bike account: + Bitte nennen Sie Ihren Namen und/oder die E-Mail Adresse Ihres Mietrad-Accounts: + + + 1. Easy access to FAQ: In the menu under Help, you can now easily search for a solution yourself for questions and problems.<br/> +2. City area vs. suburb bikes: Rental bikes that belong to the city area and cannot be returned to the suburbs are now marked by appropriate icons. Conversely, rental bikes from the suburbs cannot be returned in the city area (also marked with icons)! + 1. Einfacher Zugriff auf die FAQ: Im Menü unter Hilfe können Sie bei Fragen und Problemen nun ganz einfach selber nach einer Lösung suchen.<br/> +2. Stadtgebiet- vs. Vorort-Räder: Mieträder, die ins Stadtgebiet gehören und nicht in den Vororten zurückgegeben werden können, sind nun durch entsprechende Icons markiert. Andersherum gilt, dass Mieträder aus den Vororten nicht im Stadtgebiet zurückgegeben werden können (ebenfalls mit Icons gekennzeichnet)! + + + Easy access to FAQ: In the menu under Help, you can now easily search for a solution yourself for questions and problems. + Einfacher Zugriff auf die FAQ: Im Menü unter Hilfe können Sie bei Fragen und Problemen nun ganz einfach selber nach einer Lösung suchen. + diff --git a/TINKLib/View/IViewService.cs b/TINKLib/View/IViewService.cs index d2165e4..4115d04 100644 --- a/TINKLib/View/IViewService.cs +++ b/TINKLib/View/IViewService.cs @@ -82,7 +82,7 @@ namespace TINK.View /// Co2 saving information. /// User feedback. Task DisplayUserFeedbackPopup( - IBattery battery = null, + IBatteryMutable battery = null, string co2Saving = null); #if USCSHARP9 diff --git a/TINKLib/View/Themes/Konrad.xaml b/TINKLib/View/Themes/Konrad.xaml index 6cde385..c5d9a25 100644 --- a/TINKLib/View/Themes/Konrad.xaml +++ b/TINKLib/View/Themes/Konrad.xaml @@ -1,7 +1,9 @@ - + #D21113 @@ -134,14 +136,22 @@ - + - + - + + + + diff --git a/TINKLib/View/Themes/ShareeBike.xaml b/TINKLib/View/Themes/ShareeBike.xaml index aa140c8..f182fd7 100644 --- a/TINKLib/View/Themes/ShareeBike.xaml +++ b/TINKLib/View/Themes/ShareeBike.xaml @@ -1,7 +1,9 @@ - + #009899 @@ -149,18 +151,25 @@ + + + + + + + diff --git a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs index 6d17221..9267fa7 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; using System.Text.RegularExpressions; using TINK.Model.Bikes.BikeInfoNS.BikeNS; +using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; #if !USEFLYOUT #endif using TINK.Model.Connector; @@ -136,20 +137,32 @@ namespace TINK.ViewModel.Bikes.Bike selectedBike.PropertyChanged += (sender, eventargs) => OnSelectedBikePropertyChanged(eventargs.PropertyName); + var battery = selectedBike.Drive?.Battery; + if (battery != null) + { + battery.PropertyChanged += (_, args) => + { + if (args.PropertyName == nameof(BatteryMutable.CurrentChargeBars)) + { + RaisePropertyChanged(this, new PropertyChangedEventArgs(nameof(CurrentChargeBars))); + } + }; + } + BikesViewModel = bikesViewModel ?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null."); - OpenUrlInBrowser = openUrlInBrowser ?? (url => { Log.ForContext().Error($"No browse service avialble to upen {url}."); }); + OpenUrlInBrowser = openUrlInBrowser ?? (url => { Log.ForContext().Error($"No browse service available to open {url}."); }); } /// /// Handles BikeInfoMutable events. /// Helper member to raise events. Maps model event change notification to view model events. /// - /// - private void OnSelectedBikePropertyChanged(string p_strNameOfProp) + /// + private void OnSelectedBikePropertyChanged(string nameOfProp) { - if (p_strNameOfProp == nameof(State)) + if (nameOfProp == nameof(State)) { OnSelectedBikeStateChanged(); // Notify derived class about change of state. } @@ -211,7 +224,7 @@ namespace TINK.ViewModel.Bikes.Bike Bike.Drive.Type == Model.Bikes.BikeInfoNS.DriveNS.DriveType.Pedelec && (!Bike.Drive.Battery.IsHidden.HasValue /* no value means show battery level */ || Bike.Drive.Battery.IsHidden.Value == false); - /// Gets the image path for bike type Citybike, CargoLong, Trike or Pedelec. + /// Gets the image path for bike type City bike, CargoLong, Trike or Pedelec. public string DisplayedBikeImageSourceString => $"bike_{Bike.TypeOfBike}_{Bike.Drive.Type}_{Bike.WheelType}.png"; /// @@ -302,7 +315,7 @@ namespace TINK.ViewModel.Bikes.Bike /// /// Gets reserved into display text. /// - /// Log unexpeced states. + /// Log unexpected states. /// /// Display text private string GetReservedInfo( @@ -316,7 +329,7 @@ namespace TINK.ViewModel.Bikes.Bike /// /// Gets booked into display text. /// - /// Log unexpeced states. + /// Log unexpected states. /// /// Display text private string GetBookedInfo( @@ -366,7 +379,7 @@ namespace TINK.ViewModel.Bikes.Bike } } - /// Holds description about the tarif. + /// Holds description about the tariff. public TariffDescriptionViewModel TariffDescription => new TariffDescriptionViewModel(Bike.TariffDescription); /// Gets the value of property when PropertyChanged was fired. diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs index 5b568e0..4745d31 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs @@ -23,6 +23,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock { public Xamarin.Forms.Command ShowTrackingInfoCommand { get; private set; } public Xamarin.Forms.Command ShowRideTypeInfoCommand { get; private set; } + public Xamarin.Forms.Command ShowBikeIsBoundToCityInfoCommand { get; private set; } /// Notifies GUI about changes. public override event PropertyChangedEventHandler PropertyChanged; @@ -123,6 +124,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock }); + ShowBikeIsBoundToCityInfoCommand = new Xamarin.Forms.Command(async () => { + + // later, if value comes from backend: message = TariffDescription.CityAreaType + await ViewService.DisplayAlert( + AppResources.MessageBikeIsBoundToCityInfoTitle, + String.Format(AppResources.MessageBikeIsBoundToCityInfoText,selectedBike.TypeOfBike), + AppResources.MessageAnswerOk); + + }); + RequestHandler = user.IsLoggedIn ? RequestHandlerFactory.Create( selectedBike, @@ -178,6 +189,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock RaisePropertyChangedEvent(lastHandler); } + public bool IsBikeBoundToCity + => Bike.AaRideType == TINK.Model.Bikes.BikeInfoNS.BikeNS.AaRideType.NoAaRide ? true : false; + /// Gets visibility of the copri command button. public bool IsButtonVisible => RequestHandler.IsButtonVisible; diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/CloseLockActionViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/CloseLockActionViewModel.cs index 641e1b1..abfdb70 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/CloseLockActionViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/CloseLockActionViewModel.cs @@ -1,15 +1,10 @@ using System; using System.Threading.Tasks; using Serilog; -using TINK.Model.Connector; -using TINK.Model; using TINK.MultilingualResources; -using TINK.Repository.Exception; -using TINK.Repository.Request; using TINK.Services.Logging; using TINK.View; using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand; -using TINK.Services.BluetoothLock; using System.ComponentModel; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock @@ -33,20 +28,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock /// private IViewService ViewService { get; } - /// - /// Service to control locks. - /// - private ILocksService LockService { get; } - - /// Provides a connector object. - protected Func ConnectorFactory { get; } - - /// Delegate to retrieve connected state. - private Func IsConnectedDelegate { get; } - - /// Gets the is connected state. - bool IsConnected; - /// Object to start or stop update of view model objects from Copri. private Func ViewUpdateManager { get; } @@ -83,7 +64,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock switch (step) { case Step.StartStopingPolling: - BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; break; case Step.StartingQueryingLocation: @@ -188,12 +168,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock // 1. Step // Parameter for RentalProcess View - BikesViewModel.RentalProcess = new RentalProcess(SelectedBike.Id) + BikesViewModel.StartRentalProcess(new RentalProcessViewModel(SelectedBike.Id) { State = CurrentRentalProcess.CloseLock, StepIndex = 1, Result = CurrentStepStatus.None - }; + }); // Close Lock BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessCloseLockStepCloseLock; @@ -215,9 +195,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock await ViewUpdateManager().StartAsync(); BikesViewModel.ActionText = string.Empty; - BikesViewModel.IsIdle = true; - BikesViewModel.RentalProcess.State = CurrentRentalProcess.None; + BikesViewModel.IsIdle = true; return; } @@ -231,90 +210,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock BikesViewModel.RentalProcess.Result = CurrentStepStatus.None; BikesViewModel.RentalProcess.ImportantStepInfoText = String.Empty; - //// Ask if lock is closed - //var isLockClosed = await ViewService.DisplayAlert( - // AppResources.QuestionRentalProcessCloseLockCheckLockTitle, - // AppResources.QuestionRentalProcessCloseLockCheckLockText, - // AppResources.QuestionRentalProcessCloseLockCheckLockAnswerYes, - // AppResources.QuestionRentalProcessCloseLockCheckLockAnswerNo); + // Question if park bike or end rental + IsEndRentalRequested = await ViewService.DisplayAlert( + AppResources.QuestionRentalProcessCloseLockEndOrContinueTitle, + AppResources.QuestionRentalProcessCloseLockEndOrContinueText, + AppResources.QuestionRentalProcessCloseLockEndRentalAnswer, + AppResources.QuestionRentalProcessCloseLockContinueRentalAnswer); - //// If lock is not closed - //if(isLockClosed == false) - //{ - // var retryOrContactresult = await ViewService.DisplayAlert( - // AppResources.MessageRentalProcessCloseLockNotClosedTitle, - // AppResources.MessageRentalProcessCloseLockNotClosedText, - // AppResources.MessageAnswerRetry, - // AppResources.MessageAnswerContactSupport); + BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; - // BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; - - // if (retryOrContactresult == true) - // { - // //restart CloseLock() - // } - // else if(retryOrContactresult == false) - // { - // await OpenContactPageAsync(); - // } - //} - // If lock is closed - //else if(isLockClosed == true) - //{ - IsEndRentalRequested = await ViewService.DisplayAlert( - AppResources.QuestionRentalProcessCloseLockEndOrContinueTitle, - AppResources.QuestionRentalProcessCloseLockEndOrContinueText, - AppResources.QuestionRentalProcessCloseLockEndRentalAnswer, - AppResources.QuestionRentalProcessCloseLockContinueRentalAnswer); - - BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; - - // Continue with End rental in RequestHandler - if (IsEndRentalRequested == true) - { - return; - } - // Park bike - else if(IsEndRentalRequested == false) - { - await ViewService.DisplayAlert( - AppResources.MessageRentalProcessCloseLockFinishedTitle, - AppResources.MessageRentalProcessCloseLockFinishedText, - AppResources.MessageAnswerOk); - } - - //} + // Message for parking bike + if(IsEndRentalRequested == false) + { + await ViewService.DisplayAlert( + AppResources.MessageRentalProcessCloseLockFinishedTitle, + AppResources.MessageRentalProcessCloseLockFinishedText, + AppResources.MessageAnswerOk); + } BikesViewModel.RentalProcess.State = CurrentRentalProcess.None; BikesViewModel.IsIdle = true; return; } - /// Opens support. -//#if USEFLYOUT -// public void OpenContactPageAsync() -//#else -// public async Task OpenContactPageAsync() -//#endif -// { -// try -// { -// // Open Contact Page with Contact information for operator of SelectedBike -//#if USEFLYOUT -// ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingFeedbackAndContact); -//#else -// await ViewService.ShowPage("//ContactPage"); -//#endif -// } -// catch (Exception p_oException) -// { -// Log.Error("Ein unerwarteter Fehler ist auf der Seite Kontakt aufgetreten. Kontext: Klick auf Konakt aufnehmen bei Schloss schließen (Schloss nicht zu!). {@Exception}", p_oException); -// return; -// } -// } - /// - /// True if user requested End rental. + /// Default value of user request to end rental = false. /// private bool isEndRentalRequested = false; diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/EndRentalActionViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/EndRentalActionViewModel.cs index 3b13790..149bbb8 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/EndRentalActionViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/EndRentalActionViewModel.cs @@ -76,12 +76,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock ?? throw new ArgumentException($"Can not construct {typeof(EndRentalActionViewModel)}-object. {nameof(bikesViewModel)} must not be null."); // Set parameter for RentalProcess View to initial value. - BikesViewModel.RentalProcess = new RentalProcess(SelectedBike.Id) + BikesViewModel.StartRentalProcess(new RentalProcessViewModel(SelectedBike.Id) { State = CurrentRentalProcess.None, StepIndex = 0, Result = CurrentStepStatus.None - }; + }); } /// @@ -155,12 +155,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock // 1. Step // Parameter for RentalProcess View - BikesViewModel.RentalProcess = new RentalProcess(SelectedBike.Id) + BikesViewModel.StartRentalProcess(new RentalProcessViewModel(SelectedBike.Id) { State = CurrentRentalProcess.EndRental, StepIndex = 1, Result = CurrentStepStatus.None - }; + }); // Get Location BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessEndRentalStepGPS; @@ -271,7 +271,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock BikesViewModel.RentalProcess.ImportantStepInfoText = String.Empty; var feedBackUri = SelectedBike?.OperatorUri; - var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery); + var battery = SelectedBike.Drive?.Battery; + var feedback = await ViewService.DisplayUserFeedbackPopup( + battery, + bookingFinished?.Co2Saving); + + if (battery != null + && feedback.CurrentChargeBars != null) + { + SelectedBike.Drive.Battery.CurrentChargeBars = feedback.CurrentChargeBars; + } #endif BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; diff --git a/TINKLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/FeedbackPending.cs b/TINKLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/FeedbackPending.cs index d02d070..92c4548 100644 --- a/TINKLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/FeedbackPending.cs +++ b/TINKLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/FeedbackPending.cs @@ -62,7 +62,16 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler await ViewUpdateManager().StopAsync(); // Do get Feedback - var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery, SelectedBike?.BookingFinishedModel?.Co2Saving); + var battery = SelectedBike.Drive?.Battery; + var feedback = await ViewService.DisplayUserFeedbackPopup( + battery, + SelectedBike?.BookingFinishedModel?.Co2Saving); + + if (battery != null + && feedback.CurrentChargeBars != null) + { + SelectedBike.Drive.Battery.CurrentChargeBars = feedback.CurrentChargeBars; + } BikesViewModel.ActionText = AppResources.ActivityTextSubmittingFeedback; IsConnected = IsConnectedDelegate(); diff --git a/TINKLib/ViewModel/Bikes/BikesViewModel.cs b/TINKLib/ViewModel/Bikes/BikesViewModel.cs index f15a569..a519b74 100644 --- a/TINKLib/ViewModel/Bikes/BikesViewModel.cs +++ b/TINKLib/ViewModel/Bikes/BikesViewModel.cs @@ -10,7 +10,6 @@ using TINK.Model.Bikes; using TINK.Model.Connector; using TINK.Model.Device; using TINK.Model.User; -using TINK.MultilingualResources; using TINK.Services.BluetoothLock; using TINK.Services.Geolocation; using TINK.Services.Permissions; @@ -344,24 +343,25 @@ namespace TINK.ViewModel.Bikes } /// Used to display active rental process. - private IRentalProcess _rentalProcess = new RentalProcess(); + private IRentalProcessViewModel _rentalProcess = new RentalProcessViewModel(); /// Holds the active rental process. - public IRentalProcess RentalProcess + public IRentalProcessViewModel RentalProcess => _rentalProcess; + + /// + /// Starts the rental process. + /// + /// Rental process values to start with. + public void StartRentalProcess(IRentalProcessViewModel processViewModel) { - get => _rentalProcess; - set - { - if (value == _rentalProcess) - return; + if (processViewModel == _rentalProcess) + return; - _rentalProcess = value; + _rentalProcess.LoadFrom(processViewModel); + BikeInRentalProcess = this.FirstOrDefault(bike => bike.Id == _rentalProcess.BikeId) as Bike.BluetoothLock.BikeViewModel; - BikeInRentalProcess = this.FirstOrDefault(bike => bike.Id == _rentalProcess.BikeId) as Bike.BluetoothLock.BikeViewModel; - - base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(RentalProcess))); - base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(BikeInRentalProcess))); - } + base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(RentalProcess))); + base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(BikeInRentalProcess))); } public Bike.BluetoothLock.BikeViewModel BikeInRentalProcess { get; private set; } diff --git a/TINKLib/ViewModel/Bikes/IBikesViewModel.cs b/TINKLib/ViewModel/Bikes/IBikesViewModel.cs index 65a4366..d3fe442 100644 --- a/TINKLib/ViewModel/Bikes/IBikesViewModel.cs +++ b/TINKLib/ViewModel/Bikes/IBikesViewModel.cs @@ -4,7 +4,13 @@ namespace TINK.ViewModel.Bikes public interface IBikesViewModel { /// Holds info about active rental process. - IRentalProcess RentalProcess { get; set; } + IRentalProcessViewModel RentalProcess { get; } + + /// + /// Starts the rental process. + /// + /// Rental process values to start with. + void StartRentalProcess(IRentalProcessViewModel process); /// Holds info about current action. string ActionText { get; set; } diff --git a/TINKLib/ViewModel/Bikes/IRentalProcess.cs b/TINKLib/ViewModel/Bikes/IRentalProcessViewModel.cs similarity index 76% rename from TINKLib/ViewModel/Bikes/IRentalProcess.cs rename to TINKLib/ViewModel/Bikes/IRentalProcessViewModel.cs index b17737b..86ef584 100644 --- a/TINKLib/ViewModel/Bikes/IRentalProcess.cs +++ b/TINKLib/ViewModel/Bikes/IRentalProcessViewModel.cs @@ -2,8 +2,14 @@ using TINK.Model; namespace TINK.ViewModel.Bikes { - public interface IRentalProcess + public interface IRentalProcessViewModel { + /// + /// Loads rental process view model from source. + /// + /// Source object to load from. + void LoadFrom(IRentalProcessViewModel processViewModel); + /// /// Gets the id of the bike which is rental ends. /// diff --git a/TINKLib/ViewModel/Bikes/RentalProcess.cs b/TINKLib/ViewModel/Bikes/RentalProcessViewModel.cs similarity index 79% rename from TINKLib/ViewModel/Bikes/RentalProcess.cs rename to TINKLib/ViewModel/Bikes/RentalProcessViewModel.cs index bdd8049..955f045 100644 --- a/TINKLib/ViewModel/Bikes/RentalProcess.cs +++ b/TINKLib/ViewModel/Bikes/RentalProcessViewModel.cs @@ -21,16 +21,31 @@ namespace TINK.ViewModel.Bikes Failed = 2, } - public class RentalProcess : IRentalProcess, INotifyPropertyChanged + public class RentalProcessViewModel : IRentalProcessViewModel, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; - public RentalProcess(string bikeId = null) => BikeId = bikeId ?? string.Empty; + public RentalProcessViewModel(string bikeId = null) => BikeId = bikeId ?? string.Empty; + + /// + /// Loads rental process view model from source. + /// + /// Source object to load from + public void LoadFrom(IRentalProcessViewModel processViewModel) + { + BikeId = processViewModel.BikeId; + _state = processViewModel.State; + _stepIndex = processViewModel.StepIndex; + _stepInfoText = processViewModel.StepInfoText; + _importantStepInfoText = processViewModel.ImportantStepInfoText; + _result = processViewModel.Result; + EndRentalInfo = processViewModel.EndRentalInfo; + } /// /// Gets the id of the bike which is rental ends. /// - public string BikeId { get; } + public string BikeId { get; private set; } /// Holds info about active rental process. private CurrentRentalProcess _state = CurrentRentalProcess.None; @@ -53,9 +68,7 @@ namespace TINK.ViewModel.Bikes PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StepIndex))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result))); - } - } /// Holds info about current step in rental process. diff --git a/TINKLib/ViewModel/Contact/ContactPageViewModel.cs b/TINKLib/ViewModel/Contact/ContactPageViewModel.cs index 2d7ea69..d99c5ba 100644 --- a/TINKLib/ViewModel/Contact/ContactPageViewModel.cs +++ b/TINKLib/ViewModel/Contact/ContactPageViewModel.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using System.Windows.Input; using Plugin.Messaging; using Serilog; +using TINK.Model; using TINK.Model.Stations; using TINK.Model.Stations.StationNS; using TINK.MultilingualResources; @@ -31,6 +32,9 @@ namespace TINK.ViewModel.Info /// Holds the name of the app (sharee.bike, Mein konrad, ...) string AppFlavorName { get; } + /// Reference on the tink app instance. + private ITinkApp TinkApp { get; } + /// Holds a reference to the external trigger service. private Action OpenUrlInExternalBrowser { get; } @@ -45,6 +49,7 @@ namespace TINK.ViewModel.Info /// View service to notify user. public ContactPageViewModel( string appFlavorName, + ITinkApp tinkApp, Func createAttachment, Action openUrlInExternalBrowser, IViewService viewService) @@ -53,6 +58,9 @@ namespace TINK.ViewModel.Info ? appFlavorName : throw new ArgumentException("Can not instantiate contact page view model- object. No app name centered."); + TinkApp = tinkApp + ?? throw new ArgumentException("Can not instantiate settings page view model- object. No tink app object available."); + CreateAttachment = createAttachment ?? throw new ArgumentException("Can not instantiate contact page view model- object. No create attachment provider available."); @@ -141,7 +149,8 @@ namespace TINK.ViewModel.Info Cc = APPSUPPORTMAILADDRESS.ToUpper() != MailAddressText.ToUpper() // do not sent copy if same mail address && MailAddressText != "konrad@sharee.bike" // do not sent copy if Mein konrad ? new List { APPSUPPORTMAILADDRESS } : new List(), - Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName, SelectedStation?.Id) + Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName, SelectedStation?.Id), + Body = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}" }); return; @@ -172,7 +181,8 @@ namespace TINK.ViewModel.Info var message = new EmailMessage { To = new List { APPSUPPORTMAILADDRESS }, - Subject = SelectedStation?.Id != null ? string.Format(AppResources.SupportmailSubjectAppmailWithStation, AppFlavorName, SelectedStation?.Id) : string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName) + Subject = SelectedStation?.Id != null ? string.Format(AppResources.SupportmailSubjectAppmailWithStation, AppFlavorName, SelectedStation?.Id) : string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName), + Body = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}" }; // Send with attachment. diff --git a/TINKLib/ViewModel/FeesAndBikes/FeesAndBikesPageViewModel.cs b/TINKLib/ViewModel/FeesAndBikes/FeesAndBikesPageViewModel.cs index cac88a5..fe2158d 100644 --- a/TINKLib/ViewModel/FeesAndBikes/FeesAndBikesPageViewModel.cs +++ b/TINKLib/ViewModel/FeesAndBikes/FeesAndBikesPageViewModel.cs @@ -6,7 +6,6 @@ using TINK.Model; using TINK.Model.Bikes; using TINK.Model.Connector; using TINK.Model.Services.CopriApi; -using TINK.ViewModel.Info; using Xamarin.Forms; namespace TINK.ViewModel.Contact @@ -15,6 +14,10 @@ namespace TINK.ViewModel.Contact { public event PropertyChangedEventHandler PropertyChanged; + /// + /// Holds the current ui two letter ISO language name. + /// + private string UiIsoLanguageName { get; } /// Holds value whether site caching is on or off. bool IsSiteCachingOn { get; } @@ -71,6 +74,7 @@ namespace TINK.ViewModel.Contact string feesResourcePath, string bikesResourcePath, bool isSiteCachingOn, + string uiIsoLangugageName, Func queryProvider, Action updateUrlsAction) { @@ -80,6 +84,7 @@ namespace TINK.ViewModel.Contact IsSiteCachingOn = isSiteCachingOn; QueryProvider = queryProvider; UpdateUrlsAction = updateUrlsAction; + UiIsoLanguageName = uiIsoLangugageName; } /// Holds the name of the host. @@ -130,6 +135,13 @@ namespace TINK.ViewModel.Contact : await Task.FromResult(ViewModelHelper.FromBody("No bikes instruction resource available. Resource path is null or empty.")) }; + FAQ = new HtmlWebViewSource + { + Html = UiIsoLanguageName == "de" + ? await ViewModelHelper.GetSource($"https://sharee.bike/faq/fahrrad-nutzung/", IsSiteCachingOn) + : await ViewModelHelper.GetSource($"https://sharee.bike/faq/bike-usage/", IsSiteCachingOn) + }; + // Set state to idle. IsIdle = true; } @@ -138,6 +150,8 @@ namespace TINK.ViewModel.Contact private HtmlWebViewSource typesOfBikesText; + private HtmlWebViewSource faq; + public HtmlWebViewSource RentBikeText { get => rentBikeText; @@ -157,5 +171,15 @@ namespace TINK.ViewModel.Contact PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TypesOfBikesText))); } } + + public HtmlWebViewSource FAQ + { + get => faq; + set + { + faq = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FAQ))); + } + } } } diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfo.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfo.cs index ab0eb9d..00b788e 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfo.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfo.cs @@ -20,7 +20,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BC public TestBikeInfoSubClass( IStateInfo stateInfo, TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike bike, - Drive drive, + DriveMutable drive, bool? isDemo = DEFAULTVALUEISDEMO, IEnumerable group = null, string stationId = null, @@ -43,7 +43,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BC public void TestCtorBikeNull() { Assert.That( - () => new TestBikeInfoSubClass(new StateInfo(), null, new Drive()), + () => new TestBikeInfoSubClass(new StateInfo(), null, new DriveMutable()), Throws.ArgumentNullException); } diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfoMutable.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfoMutable.cs index 20019cf..bc3ff32 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfoMutable.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeInfoMutable.cs @@ -30,7 +30,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike Func dateTimeProvider = null, IStateInfo stateInfo = null) : base( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike(id, lockModel, wheelType, typeOfBike, aaRideType, description), - new TINK.Model.Bikes.BikeInfoNS.DriveNS.Drive(), + new TINK.Model.Bikes.BikeInfoNS.DriveNS.DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, isDemo, group, diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeMutable.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeMutable.cs index cef2fa9..1ab2cf7 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeMutable.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeMutable.cs @@ -28,7 +28,7 @@ namespace TestTINKLib Func dateTimeProvider = null, IStateInfo stateInfo = null) : base( new Bike(id, lockType, wheelType, typeOfBike, aaRideType, description), - new TINK.Model.Bikes.BikeInfoNS.DriveNS.Drive(), + new TINK.Model.Bikes.BikeInfoNS.DriveNS.DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, isDemo, group, diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeViewModel.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeViewModel.cs index 021aba2..c621578 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeViewModel.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/BC/TestBikeViewModel.cs @@ -31,7 +31,7 @@ namespace UITest.Fixtures.ViewModel Func dateTimeProvider = null, IStateInfo stateInfo = null) : base( new Bike(id, lockModel, wheelType, typeOfBike, aaRideType, description), - new TINK.Model.Bikes.BikeInfoNS.DriveNS.Drive(), + new TINK.Model.Bikes.BikeInfoNS.DriveNS.DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, pisDemo, group, diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/TestGetBikeLocationCommand.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/TestGetBikeLocationCommand.cs index fb15576..2797810 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/TestGetBikeLocationCommand.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/Command/TestGetBikeLocationCommand.cs @@ -10,6 +10,7 @@ using TINK.Model.Connector; using TINK.Model.Device; using TINK.Model.State; using TINK.Model.User; +using TINK.Repository.Request; using TINK.Services.BluetoothLock; using TINK.Services.Geolocation; using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand; @@ -199,5 +200,39 @@ namespace TestShareeLib.Model.Bikes.BikeInfoNS.BluetoothLock.Command await listener.ReportStateAsync(State.QueryLocationSucceeded, string.Empty); }); } + + /// + /// Use case: End rental. + /// Final state: Disposable closed + /// + [Test] + public async Task TestReturnClosingLockLocationAvailable() + { + var bike = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var listener = Substitute.For(); + + var closingLockLocation = Substitute.For(); + closingLockLocation.Latitude.Returns(7); + closingLockLocation.Longitude.Returns(9); + closingLockLocation.Timestamp.Returns(DateTimeOffset.MinValue); + + bike.LockInfo.Location.Returns((IGeolocation)closingLockLocation); // When locking bike geolocation was available. + + var lockingLocation = await InvokeAsync( + bike, + geolocation, + locks, + () => DateTime.MinValue, + listener); + + // Verify behavior + Received.InOrder(() => + { + listener.ReportStep(Step.StartingQueryLocation); + return; + }); + } } } diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfo.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfo.cs index dd5919d..01911d7 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfo.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfo.cs @@ -19,9 +19,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock [Test] public void TestCtorAvailable() { - Assert.AreEqual("12", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").Id); - Assert.AreEqual("13", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").StationId); - Assert.AreEqual(InUseStateEnum.Disposable, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").State.Value); + Assert.AreEqual("12", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").Id); + Assert.AreEqual("13", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").StationId); + Assert.AreEqual(InUseStateEnum.Disposable, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").State.Value); } [Test] @@ -30,7 +30,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock Assert.That( () => new BikeInfo( null, - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), @@ -41,9 +41,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock [Test] public void TestCtorRequested() { - Assert.AreEqual("12", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1), false /*isDemo*/, null /*group*/).Id); - Assert.AreEqual(112, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1), false /*isDemo*/, null /*group*/).LockInfo.Id); - Assert.AreEqual(InUseStateEnum.Reserved, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1), false /*isDemo*/, null /*group*/).State.Value); + Assert.AreEqual("12", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1), false /*isDemo*/, null /*group*/).Id); + Assert.AreEqual(112, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1), false /*isDemo*/, null /*group*/).LockInfo.Id); + Assert.AreEqual(InUseStateEnum.Reserved, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1), false /*isDemo*/, null /*group*/).State.Value); } [Test] @@ -52,7 +52,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock Assert.That( () => new BikeInfo( null, - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), @@ -73,9 +73,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock [Test] public void TestCtorBooked() { - Assert.AreEqual("12", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).Id); - Assert.AreEqual(112, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).LockInfo.Id); - Assert.AreEqual(InUseStateEnum.Booked, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).State.Value); + Assert.AreEqual("12", new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).Id); + Assert.AreEqual(112, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).LockInfo.Id); + Assert.AreEqual(InUseStateEnum.Booked, new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("12", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).State.Value); } [Test] @@ -84,7 +84,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock Assert.That( () => new BikeInfo( null, - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 112, new Guid(), diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfoMutalbe.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfoMutalbe.cs index e404214..642b7fb 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfoMutalbe.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/BluetoothLock/TestBikeInfoMutalbe.cs @@ -27,7 +27,7 @@ namespace TestShareeLib.Model.Bike.BluetoothLock new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike( "MyBikeId", TINK.Model.Bikes.BikeInfoNS.BikeNS.LockModel.ILockIt), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 42, new Guid(), @@ -52,7 +52,7 @@ namespace TestShareeLib.Model.Bike.BluetoothLock TINK.Model.Bikes.BikeInfoNS.BikeNS.LockModel.ILockIt, TINK.Model.Bikes.BikeInfoNS.BikeNS.WheelType.Trike, TINK.Model.Bikes.BikeInfoNS.BikeNS.TypeOfBike.Cargo), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 42, new Guid(), diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/CopriLock/TestBikeInfo.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/CopriLock/TestBikeInfo.cs index 408c519..6238247 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/CopriLock/TestBikeInfo.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/CopriLock/TestBikeInfo.cs @@ -15,7 +15,7 @@ namespace TestShareeLib.Model.Bike.CopriLock { var bike = new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("bikeId", LockModel.Sigo), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "stationId", new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()); @@ -32,7 +32,7 @@ namespace TestShareeLib.Model.Bike.CopriLock Assert.That( () => new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( null, - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "stationId", new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()), @@ -44,7 +44,7 @@ namespace TestShareeLib.Model.Bike.CopriLock { var bike = new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("bikeId", LockModel.Sigo), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "stationId", new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo(), @@ -61,7 +61,7 @@ namespace TestShareeLib.Model.Bike.CopriLock Assert.That( () => new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( null, - new Drive(), + new DriveMutable(), DataSource.Copri, DateTime.Now, "a@b", @@ -79,7 +79,7 @@ namespace TestShareeLib.Model.Bike.CopriLock Assert.That( () => new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( null, - new Drive(), + new DriveMutable(), DataSource.Copri, DateTime.Now, "a@b", diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/TestBatteryMutable.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/TestBatteryMutable.cs new file mode 100644 index 0000000..2f17301 --- /dev/null +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/DriveNS/BatteryNS/TestBatteryMutable.cs @@ -0,0 +1,93 @@ +using NUnit.Framework; +using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; + +namespace TestShareeLib.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS +{ + [TestFixture] + public class TestBatteryMutable + { + [Test] + public void TestCurrentChargeBars() + { + var battery = new BatteryMutable(new Battery.Builder { + MaxChargeBars = 5, + CurrentChargeBars = 1, + CurrentChargePercent = 20.0 + }.Build()); + + battery.CurrentChargeBars = 4; + + Assert.That( + battery.CurrentChargeBars, + Is.EqualTo(4)); + + Assert.That( + battery.CurrentChargePercent, + Is.EqualTo(80.0)); + } + + [Test] + public void TestCurrentChargeBarsNull() + { + var battery = new BatteryMutable(new Battery.Builder + { + MaxChargeBars = 5, + CurrentChargeBars = 1, + CurrentChargePercent = 20.0 + }.Build()); + + battery.CurrentChargeBars = null; + + Assert.That( + battery.CurrentChargeBars, + Is.Null); + + Assert.That( + battery.CurrentChargePercent, + Is.EqualTo(double.NaN)); + } + + [Test] + public void TestCurrentChargeBarsInvalidStateZero() + { + var battery = new BatteryMutable(new Battery.Builder + { + MaxChargeBars = 0, // Could lead to division by zero if not handled correctly. + CurrentChargeBars = null, + CurrentChargePercent = 20.0 + }.Build()); + + battery.CurrentChargeBars = 4; + + Assert.That( + battery.CurrentChargeBars, + Is.Null); + + Assert.That( + battery.CurrentChargePercent, + Is.EqualTo(20.0)); + } + + + [Test] + public void TestCurrentChargeBarsInvalidStateNull() + { + var battery = new BatteryMutable(new Battery.Builder + { + MaxChargeBars = null, + CurrentChargeBars = null, + CurrentChargePercent = 20.0 + }.Build()); + + battery.CurrentChargeBars = 4; + + Assert.That( + battery.CurrentChargeBars, + Is.Null); + + Assert.That( + battery.CurrentChargePercent, + Is.EqualTo(20.0)); + } + } +} diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/DriveNS/TestDriveMutable.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/DriveNS/TestDriveMutable.cs index fdc6123..22c5921 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/DriveNS/TestDriveMutable.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/DriveNS/TestDriveMutable.cs @@ -1,4 +1,4 @@ -using NSubstitute; +using NSubstitute; using NUnit.Framework; using TINK.Model.Bikes.BikeInfoNS.DriveNS; using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS; @@ -12,7 +12,7 @@ namespace TestShareeLib.Model.BikeInfo.DriveNS [Test] public void TestCtorNoArgs() { - var drive = new TINK.Model.Bikes.BikeInfoNS.DriveNS.Drive(); + var drive = new DriveMutable(); Assert.That( drive.Type, Is.EqualTo(DriveType.SoleHumanPowered)); @@ -35,7 +35,7 @@ namespace TestShareeLib.Model.BikeInfo.DriveNS engine.Manufacturer.Returns("Bosch"); battery.CurrentChargePercent.Returns(97); - var drive = new TINK.Model.Bikes.BikeInfoNS.DriveNS.Drive(engine, battery); + var drive = new DriveMutable(engine, battery); Assert.That( drive.Engine.Manufacturer, diff --git a/TestShareeLib/Model/Bikes/BikeInfoNS/TestBikeCollectionMutable.cs b/TestShareeLib/Model/Bikes/BikeInfoNS/TestBikeCollectionMutable.cs index af9921e..f91007e 100644 --- a/TestShareeLib/Model/Bikes/BikeInfoNS/TestBikeCollectionMutable.cs +++ b/TestShareeLib/Model/Bikes/BikeInfoNS/TestBikeCollectionMutable.cs @@ -41,7 +41,7 @@ namespace TestTINKLib Func dateTimeProvider = null, IStateInfo stateInfo = null) : base( new Bike(id, lockType, wheelType, typeOfBike, aaRideType, description), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, isDemo, group, @@ -132,7 +132,7 @@ namespace TestTINKLib { new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike("57", LockModel.ILockIt), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, DateTime.Now, "john@long", @@ -141,7 +141,7 @@ namespace TestTINKLib null), new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike("20", LockModel.ILockIt), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, DateTime.Now, "john@long", @@ -150,7 +150,7 @@ namespace TestTINKLib null), new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike("33", LockModel.Sigo), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "7", new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()), @@ -186,7 +186,7 @@ namespace TestTINKLib { new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike("57", LockModel.Sigo), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "7" /*station id*/, new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()), @@ -226,7 +226,7 @@ namespace TestTINKLib { new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new Bike("57" /* bike id*/, LockModel.ILockIt, WheelType.Trike, TypeOfBike.Allround, AaRideType.NoAaRide, "Test description"), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 17, /* Lock id */ new Guid(), @@ -271,7 +271,7 @@ namespace TestTINKLib { new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike("57", LockModel.Sigo), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "7" /*station id*/, new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo(), diff --git a/TestShareeLib/Model/Connector/Updater/TestUpdaterJSON.cs b/TestShareeLib/Model/Connector/Updater/TestUpdaterJSON.cs index a5b35d0..801037b 100644 --- a/TestShareeLib/Model/Connector/Updater/TestUpdaterJSON.cs +++ b/TestShareeLib/Model/Connector/Updater/TestUpdaterJSON.cs @@ -653,7 +653,7 @@ namespace TestShareeLib.Model.Connector.Updater () => Substitute.For(), new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("17", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, 22, new Guid("0000f00d-1212-efde-1523-785fef13d123"), @@ -699,7 +699,7 @@ namespace TestShareeLib.Model.Connector.Updater () => Substitute.For(), new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("17", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, 22, new Guid("0000f00d-1212-efde-1523-785fef13d123"), @@ -741,7 +741,7 @@ namespace TestShareeLib.Model.Connector.Updater () => Substitute.For(), new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("17", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, 22, new Guid("0000f00d-1212-efde-1523-785fef13d123"), diff --git a/TestShareeLib/Model/TestBikeCollectionFilter.cs b/TestShareeLib/Model/TestBikeCollectionFilter.cs index e6d519c..bedcfaa 100644 --- a/TestShareeLib/Model/TestBikeCollectionFilter.cs +++ b/TestShareeLib/Model/TestBikeCollectionFilter.cs @@ -20,21 +20,21 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike { {"3", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("7", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, 17, /* Lock id */ new Guid(), "3" /* Station id */) }, {"7", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("8", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, 17, /* Lock id */ new Guid(), "12" /* Station id */) }, {"12", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("33", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, 17, /* Lock id */ new Guid(), @@ -78,12 +78,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike new Dictionary { {"7", new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("8", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, "12", /* Station id */ new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()) }, {"12", new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("33", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, "12", /* Station id */ new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()) } @@ -103,18 +103,18 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike new Dictionary { {"7", new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("8", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, "12", /* Station id */ new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()) }, {"11", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("33", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "12" /* Station id */) }, {"12", new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("33", LockModel.ILockIt), - new Drive(), + new DriveMutable(), DataSource.Copri, "12", /* Station id */ new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo()) } diff --git a/TestShareeLib/Services/BluetoothLock/TestLockServiceSimulation.cs b/TestShareeLib/Services/BluetoothLock/TestLockServiceSimulation.cs index 9a24f01..0cf18b7 100644 --- a/TestShareeLib/Services/BluetoothLock/TestLockServiceSimulation.cs +++ b/TestShareeLib/Services/BluetoothLock/TestLockServiceSimulation.cs @@ -20,8 +20,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.Service.LockService var bikes = new BikeCollection(new Dictionary() { - { "42", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 1, new Guid(),new byte[] { 1, 4 }, new byte[] { 3, 4 }, new byte[] { 3, 4 }, DateTime.Now, "a@b", "1" , null /*operator uri*/) }, - { "43", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("43", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 3, new Guid(),new byte[] { 4, 4 }, new byte[] { 4, 7 }, new byte[] { 5, 4 }, DateTime.Now, "c@b", "1" , null /*operator uri*/) } + { "42", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 1, new Guid(),new byte[] { 1, 4 }, new byte[] { 3, 4 }, new byte[] { 3, 4 }, DateTime.Now, "a@b", "1" , null /*operator uri*/) }, + { "43", new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("43", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 3, new Guid(),new byte[] { 4, 4 }, new byte[] { 4, 7 }, new byte[] { 5, 4 }, DateTime.Now, "c@b", "1" , null /*operator uri*/) } } ); diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BC/TestBikeAtStationViewModel.cs b/TestShareeLib/ViewModel/Bikes/Bike/BC/TestBikeAtStationViewModel.cs index 85a48b5..2c50bd2 100644 --- a/TestShareeLib/ViewModel/Bikes/Bike/BC/TestBikeAtStationViewModel.cs +++ b/TestShareeLib/ViewModel/Bikes/Bike/BC/TestBikeAtStationViewModel.cs @@ -35,7 +35,7 @@ namespace UITest.Fixtures.ViewModel Func dateTimeProvider = null, IStateInfo stateInfo = null) : base( new Bike(id, lockModel, wheelType, typeOfBike, aaRideType, description), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, isDemo, group, diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestCloseLockActionViewModel.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestCloseLockActionViewModel.cs new file mode 100644 index 0000000..97361b9 --- /dev/null +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestCloseLockActionViewModel.cs @@ -0,0 +1,212 @@ +using System.Threading.Tasks; +using NSubstitute; +using NUnit.Framework; +using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; +using TINK.Model.Connector; +using TINK.Model.State; +using TINK.Services.BluetoothLock; +using TINK.View; +using TINK.ViewModel; +using TINK.ViewModel.Bikes; +using TINK.ViewModel.Bikes.Bike.BluetoothLock; +using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; +using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand; + +namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock +{ + [TestFixture] + public class TestCloseLockActionViewModel + { + /// + /// Use case: Close lock. + /// Final state: Occupied closed, End rental requested + /// + [Test] + public async Task TestCloseLockAndRequestEndRental() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var listener = Substitute.For(); + + var closeLockActionViewModel = new CloseLockActionViewModel( + bike, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + bike.CloseLockAsync(Arg.Any(), Arg.Any()).Returns(x => + { + // Add calls to ReportStep which IBikeInfoMutable implementation would do. + closeLockActionViewModel.ReportStep(Step.StartStopingPolling); + closeLockActionViewModel.ReportStep(Step.StartingQueryingLocation); + closeLockActionViewModel.ReportStep(Step.ClosingLock); + closeLockActionViewModel.ReportStep(Step.WaitStopPollingQueryLocation); + closeLockActionViewModel.ReportStep(Step.QueryLocationTerminated); + closeLockActionViewModel.ReportStep(Step.UpdateLockingState); + return Task.CompletedTask; + }); + + bike.State.Value.Returns(InUseStateEnum.Booked); // Request handler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object. + + await closeLockActionViewModel.CloseLockAsync(); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + + bikesViewModel.StartRentalProcess(Arg.Is( + x => x.BikeId == "0" + && x.State == CurrentRentalProcess.CloseLock + && x.StepIndex == 1 + && x.Result == CurrentStepStatus.None)); + + //Step.StartingQueryingLocation + bikesViewModel.ActionText = "Start query location..."; + //Step.ClosingLock + bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; + //Step.WaitStopPollingQueryLocation + bikesViewModel.ActionText = "Query location..."; + //Step.UpdateLockingState + bikesViewModel.ActionText = "Updating lock state..."; + + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + + bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; + + bikesViewModel.RentalProcess.StepIndex = 2; + bikesViewModel.RentalProcess.Result = CurrentStepStatus.None; + + //Ask whether park bike or end rental + viewService.DisplayAlert( + "Please choose", + "Do you want to park the bike to continue riding later or return the bike now and end the rental?", + "End rental", + "Park bike" + ); + + closeLockActionViewModel.IsEndRentalRequested = true; + + bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; + + bikesViewModel.RentalProcess.State = CurrentRentalProcess.None; + + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + return; + }); + + } + + /// + /// Use case: Close lock. + /// Final state: Occupied closed, Park bike requested + /// + [Test] + public async Task TestCloseLockAndRequestParkBike() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var listener = Substitute.For(); + + var closeLockActionViewModel = new CloseLockActionViewModel( + bike, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + bike.CloseLockAsync(Arg.Any(), Arg.Any()).Returns(x => + { + // Add calls to ReportStep which IBikeInfoMutable implementation would do. + closeLockActionViewModel.ReportStep(Step.StartStopingPolling); + closeLockActionViewModel.ReportStep(Step.StartingQueryingLocation); + closeLockActionViewModel.ReportStep(Step.ClosingLock); + closeLockActionViewModel.ReportStep(Step.WaitStopPollingQueryLocation); + closeLockActionViewModel.ReportStep(Step.QueryLocationTerminated); + closeLockActionViewModel.ReportStep(Step.UpdateLockingState); + return Task.CompletedTask; + }); + + bike.State.Value.Returns(InUseStateEnum.Booked); // Request handler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object. + + await closeLockActionViewModel.CloseLockAsync(); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + + bikesViewModel.StartRentalProcess(Arg.Is( + x => x.BikeId == "0" + && x.State == CurrentRentalProcess.CloseLock + && x.StepIndex == 1 + && x.Result == CurrentStepStatus.None)); + + //Step.StartingQueryingLocation + bikesViewModel.ActionText = "Start query location..."; + //Step.ClosingLock + bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; + //Step.WaitStopPollingQueryLocation + bikesViewModel.ActionText = "Query location..."; + //Step.UpdateLockingState + bikesViewModel.ActionText = "Updating lock state..."; + + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + + bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; + + bikesViewModel.RentalProcess.StepIndex = 2; + bikesViewModel.RentalProcess.Result = CurrentStepStatus.None; + + //Ask whether park bike or end rental + viewService.DisplayAlert( + "Please choose", + "Do you want to park the bike to continue riding later or return the bike now and end the rental?", + "End rental", + "Park bike" + ); + + closeLockActionViewModel.IsEndRentalRequested = false; + + bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; + + //Show confirmation + viewService.DisplayAlert( + "Lock is closed", + "Bike is parked. Your chargeable rental continues!", + "OK" + ); + + bikesViewModel.RentalProcess.State = CurrentRentalProcess.None; + + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + return; + }); + + } + + } +} diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestEndRentalActionViewModel.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestEndRentalActionViewModel.cs new file mode 100644 index 0000000..ab2ef4a --- /dev/null +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestEndRentalActionViewModel.cs @@ -0,0 +1,490 @@ +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; +using NSubstitute; +using NSubstitute.ExceptionExtensions; +using NUnit.Framework; +using TINK.Model; +using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; +using TINK.Model.Connector; +using TINK.Model.State; +using TINK.Model.User; +using TINK.Repository.Exception; +using TINK.Repository.Request; +using TINK.Repository.Response; +using TINK.Services.BluetoothLock; +using TINK.Services.Geolocation; +using TINK.View; +using TINK.ViewModel; +using TINK.ViewModel.Bikes; +using TINK.ViewModel.Bikes.Bike.BluetoothLock; +using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; +using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand; + +namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock +{ + [TestFixture] + public class TestEndRentalActionViewModel + { + /// + /// Use case: End rental. + /// Initial state: No LastGeolocation from CloseLock, Lock currently out of reach + /// Final state: Booked, lock state unknown. + /// + /// Replaces test TestReturnOutOfReach which was removed for sharee.bike version ~3.0.362. + [Test] + public async Task TestReturnLastGeolocatonNullLockOutOfReach() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var returnBikeActionViewModel = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + bike.GetLockedBikeLocationAsync(Arg.Any()).Throws((x) => + { + returnBikeActionViewModel.ReportStep(Step.StartingQueryLocation); + returnBikeActionViewModel.ReportStateAsync(State.DisconnetedNoLocationError, "").Wait(); + returnBikeActionViewModel.ReportStep(Step.DisconnectingLockOnDisconnectedNoLocationError); + returnBikeActionViewModel.ReportStateAsync(State.QueryLocationSucceeded, "").Wait(); + throw new Exception(); + }); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + await returnBikeActionViewModel.EndRentalAsync(); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + viewService.DisplayAlert( + "Rental could not be terminated!", + "We could not assign the bike to any station. For this we need your location information while you are standing right next to the bike. Only then your rental can be terminated!\r\n\r\nApproach the bike lock, turn on Bluetooth and Location services and try again.", + "OK"); + bikesViewModel.ActionText = "Disconnecting lock..."; + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.That( + bike.LockInfo.State, + Is.EqualTo(LockingState.UnknownDisconnected)); + } + + /// + /// Use case: End rental. + /// Initial state: No LastGeolocation from CloseLock, Location services not reachable + /// Final state: Booked, lock state unknown. + /// + /// Replaces test TestReturnLockInReachNoGeolocation which was removed for sharee.bike older ~ 3.0.362. + [Test] + public void TestReturnStartGetGeolocationException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + bike.GetLockedBikeLocationAsync(Arg.Any()).Throws((x) => + { + handler.ReportStep(Step.StartingQueryLocation); + handler.ReportStateAsync(State.QueryLocationFailed, "Ups...").Wait(); + throw new Exception(); + }); + + bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state does not change. + + bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. + + var subsequent = handler.EndRentalAsync(); + + // Verify behavior + Received.InOrder(async () => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "Query location..."; + await viewService.DisplayAlert( + "Rental could not be terminated!", + "Grant Location permission, activate Location services and try again.", + "OK"); + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + } + + /// + /// Use case: End rental. + /// Initial state: No LastGeolocation from CloseLock, Lock currently in reach + /// Final state: Disposable closed + /// + [Test] + public async Task TestReturnLockInReach() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var listener = Substitute.For(); + + var returnBikeActionViewModel = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + bike.GetLockedBikeLocationAsync(Arg.Any()).Returns((x) => + { + returnBikeActionViewModel.ReportStep(Step.StartingQueryLocation); + returnBikeActionViewModel.ReportStateAsync(State.QueryLocationSucceeded, string.Empty).Wait(); + return Task.FromResult(new LocationDto.Builder().Build()); + }); + + bike.State.Value.Returns(InUseStateEnum.Disposable); // Request handler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object. + + await returnBikeActionViewModel.EndRentalAsync(); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "Query location..."; + bikesViewModel.ActionText = "One moment please..."; + connector.Command.DoReturn(bike, Arg.Is(x => x != null)); + bikesViewModel.ActionText = "Disconnecting lock..."; + locks.DisconnectAsync(Arg.Any(), Arg.Any()); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + } + + /// + /// Use case: End rental. + /// Final state: Same as initial state. + /// + [Test] + public async Task TestReturnReturnFailsWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + + var handler = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. + + connector.Command.DoReturn(bike, Arg.Any()).Returns(x => throw new WebConnectFailureException("Context info", new Exception("hoppla"))); + + bike.State.Value.Returns(InUseStateEnum.Booked); // RequestHandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // RequestHandler factory queries lock state to create appropriate request handler object. + + await handler.EndRentalAsync(); + + await locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + await locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "One moment please..."; + connector.Command.DoReturn(bike, Arg.Any()); + bikesViewModel.ActionText = string.Empty; + viewService.DisplayAlert( + "Rental could not be terminated!", + "A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. Try again.", + "OK"); + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + } + + /// + /// Use case: End rental. + /// Final state: Same as initial state. + /// + [Test] + public async Task TestReturnReturnFailsNotAtStationException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + + var handler = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + + locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. + + NotAtStationException.IsNotAtStation("Failure 2178: bike 1545 out of GEO fencing. 15986 meter distance to next station 42. OK: bike 1545 locked confirmed", out NotAtStationException notAtStationException); + + connector.Command.DoReturn(bike, Arg.Any()).Returns(x => + throw notAtStationException); + + bike.State.Value.Returns(InUseStateEnum.Booked); // RequestHandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // RequestHandler factory queries lock state to create appropriate request handler object. + + await handler.EndRentalAsync(); + + await locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "One moment please..."; + connector.Command.DoReturn(bike, Arg.Any()); + bikesViewModel.ActionText = string.Empty; + viewService.DisplayAlert("Rental could not be terminated!", "End rental outside or at wrong station is not possible. Distance to next suitable station 42 is 15986 m.", "OK"); + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + } + + /// + /// Use case: End rental. + /// Initial state: Copri reports that no GPS data available + /// Final state: Same as initial state. + /// + [Test] + public async Task TestReturnReturnFailsNoGPSDataException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + + var handler = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. + + NoGPSDataException.IsNoGPSData("Failure 2245: No GPS data, state change forbidden.", out NoGPSDataException noGPSDataException); + + connector.Command.DoReturn(bike, Arg.Any()).Returns(x => + throw noGPSDataException); + + bike.State.Value.Returns(InUseStateEnum.Booked); // RequestHandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // RequestHandler factory queries lock state to create appropriate request handler object. + + await handler.EndRentalAsync(); + + await locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "One moment please..."; + connector.Command.DoReturn(bike, Arg.Any()); + bikesViewModel.ActionText = string.Empty; + viewService.DisplayAlert("Rental could not be terminated!", "End rental at an unknown location is not possible.\r\nRental can be ended if\r\n- location information is available when closing lock\r\n- bike is in reach and location information is available when pressing button \"End rental\"", "OK"); + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + } + + /// + /// Use case: End rental. + /// Final state: Same as initial state. + /// + [Test] + public async Task TestReturnReturnFailsResponseException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + + var handler = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. + + connector.Command.DoReturn(bike, Arg.Any()).Returns(x => + throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); // RequestHandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // RequestHandler factory queries lock state to create appropriate request handler object. + + await handler.EndRentalAsync(); + + await locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "One moment please..."; + connector.Command.DoReturn(bike, Arg.Any()); + bikesViewModel.ActionText = string.Empty; + viewService.DisplayAdvancedAlert("Rental could not be terminated!", "Outer message.", "Some invalid data received!", "OK"); + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + } + + /// + /// Use case: End rental. + /// Final state: Same as initial state. + /// + [Test] + public async Task TestReturnReturnFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + + var returnBikeActionViewModel = new EndRentalActionViewModel( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + locks, + () => pollingManager, + viewService, + bikesViewModel); + + bike.Id.Returns("0"); + + locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. + + connector.Command.DoReturn(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); // Request handler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object. + + await returnBikeActionViewModel.EndRentalAsync(); + + await locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behavior + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "One moment please..."; + connector.Command.DoReturn(bike, Arg.Any()); + bikesViewModel.ActionText = string.Empty; + viewService.DisplayAlert( + "Rental could not be terminated!", + "Exception message.", + "OK" + ); + pollingManager.StartAsync(); // polling must be restarted again + bikesViewModel.ActionText = string.Empty; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + } + + } +} diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestRequestHandlerFactory.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestRequestHandlerFactory.cs index e14a311..0caf1d0 100644 --- a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestRequestHandlerFactory.cs +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/TestRequestHandlerFactory.cs @@ -36,7 +36,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "12"), + new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "12"), "My Station Name"); Assert.AreEqual(InUseStateEnum.Disposable, bike.State.Value); Assert.AreEqual(LockingState.UnknownDisconnected, bike.LockInfo.State); @@ -61,7 +61,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id */, new Guid(), /*K User*/ null, /*K Admin*/ null, /*K Seed*/ null, new DateTime(2020, 1, 1), "a@b.com", "12", null, null, () => new DateTime(2020, 1, 1), false, new List()), + new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id */, new Guid(), /*K User*/ null, /*K Admin*/ null, /*K Seed*/ null, new DateTime(2020, 1, 1), "a@b.com", "12", null, null, () => new DateTime(2020, 1, 1), false, new List()), "My Station Name"); Assert.AreEqual( typeof(ReservedDisconnected), @@ -84,7 +84,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b.com", "12", null, null, () => new DateTime(2020, 1, 1), false, new List()), + new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b.com", "12", null, null, () => new DateTime(2020, 1, 1), false, new List()), "My Station Name"); bike.LockInfo.State = LockingState.Closed; Assert.AreEqual( @@ -108,7 +108,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null /* user key */, null, null, new DateTime(2020, 1, 1), "a@b.com", "12", null, null, () => new DateTime(2020, 1, 1), false, new List()), + new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null /* user key */, null, null, new DateTime(2020, 1, 1), "a@b.com", "12", null, null, () => new DateTime(2020, 1, 1), false, new List()), "My Station Name"); bike.LockInfo.State = LockingState.Open; Assert.AreEqual( @@ -132,7 +132,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new System.DateTime(2020, 1, 1), "a@b.com", "12", null /*operator uri*/, null, false, new List()), + new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new System.DateTime(2020, 1, 1), "a@b.com", "12", null /*operator uri*/, null, false, new List()), "My Station Name"); bike.LockInfo.State = LockingState.Closed; Assert.AreEqual( @@ -156,7 +156,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new System.DateTime(2020, 1, 1), "a@b.com", "12", null /*operator uri*/, null, false, new List()), + new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new System.DateTime(2020, 1, 1), "a@b.com", "12", null /*operator uri*/, null, false, new List()), "My Station Name"); bike.LockInfo.State = LockingState.Open; Assert.AreEqual( @@ -180,7 +180,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new System.DateTime(2020, 1, 1), "a@b.com", "12", null /*operator uri*/, null, false, new List()), + new BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("22", LockModel.ILockIt, WheelType.Mono, TypeOfBike.Allround), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 0 /* lock Id*/, new Guid(), null, null, null, new System.DateTime(2020, 1, 1), "a@b.com", "12", null /*operator uri*/, null, false, new List()), "My Station Name"); Assert.AreEqual( diff --git a/TestShareeLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/TestFeedbackPending.cs b/TestShareeLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/TestFeedbackPending.cs index bc63af6..429843e 100644 --- a/TestShareeLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/TestFeedbackPending.cs +++ b/TestShareeLib/ViewModel/Bikes/Bike/CopriLock/RequestHandler/TestFeedbackPending.cs @@ -55,7 +55,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action - viewService.DisplayUserFeedbackPopup(Arg.Any(), Arg.Any()); + viewService.DisplayUserFeedbackPopup(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "Submitting feedback..."; connector.Command.DoSubmitFeedback(Arg.Any(), Arg.Any()); // Booking must be performed bikesViewModel.ActionText = string.Empty; @@ -117,7 +117,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action - viewService.DisplayUserFeedbackPopup(Arg.Any(), Arg.Any()); + viewService.DisplayUserFeedbackPopup(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "Submitting feedback..."; connector.Command.DoSubmitFeedback(Arg.Any(), Arg.Any()); // Booking must be performed bikesViewModel.ActionText = "Updating..."; @@ -176,7 +176,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action - viewService.DisplayUserFeedbackPopup(Arg.Any(), Arg.Any()); + viewService.DisplayUserFeedbackPopup(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "Submitting feedback..."; connector.Command.DoSubmitFeedback(Arg.Any(), Arg.Any()); // Booking must be performed viewService.PushModalAsync(ViewTypes.MiniSurvey); diff --git a/TestShareeLib/ViewModel/Bikes/Bike/TestBikeViewModelFactory.cs b/TestShareeLib/ViewModel/Bikes/Bike/TestBikeViewModelFactory.cs index 2a0746e..56e0a4d 100644 --- a/TestShareeLib/ViewModel/Bikes/Bike/TestBikeViewModelFactory.cs +++ b/TestShareeLib/ViewModel/Bikes/Bike/TestBikeViewModelFactory.cs @@ -61,7 +61,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel () => false /* not connected */, (_) => Substitute.For(), () => Substitute.For(), - new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", LockModel.ILockIt), new Drive(), DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "42"), "My Station Name"), + new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", LockModel.ILockIt), new DriveMutable(), DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "42"), "My Station Name"), Substitute.For(), // user null /*ViewContext*/, Substitute.For(), @@ -83,7 +83,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel null, // viewUpdateManager NSubstitute.Substitute.For(), null, // viewService - new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfoMutable(new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", LockModel.ILockIt), new Drive(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "17", new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = TINK.Model.Bikes.BikeInfoNS.CopriLock.LockingState.Closed }.Build()), "My Station Name"), + new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfoMutable(new TINK.Model.Bikes.BikeInfoNS.CopriLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", LockModel.ILockIt), new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, "17", new TINK.Model.Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = TINK.Model.Bikes.BikeInfoNS.CopriLock.LockingState.Closed }.Build()), "My Station Name"), NSubstitute.Substitute.For(), // user new ViewContext(PageContext.BikesAtStation, "FR1012"), // Context does not matter for this test. NSubstitute.Substitute.For(), @@ -104,7 +104,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel null, // viewUpdateManager NSubstitute.Substitute.For(), null, // viewService - new BikeInfoMutableUnsupported(new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", TINK.Model.Bikes.BikeInfoNS.BikeNS.LockModel.ILockIt), new Drive(), DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "42"), "My Station Name"), + new BikeInfoMutableUnsupported(new TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo(new TINK.Model.Bikes.BikeInfoNS.BikeNS.Bike("42", TINK.Model.Bikes.BikeInfoNS.BikeNS.LockModel.ILockIt), new DriveMutable(), DataSource.Copri, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "42"), "My Station Name"), NSubstitute.Substitute.For(), // user new ViewContext(PageContext.BikesAtStation, "FR1012"), // Context does not matter for this test. NSubstitute.Substitute.For(), diff --git a/TestShareeLib/ViewModel/Map/TestMapPageViewModel.cs b/TestShareeLib/ViewModel/Map/TestMapPageViewModel.cs index 9cd06c1..8d6c109 100644 --- a/TestShareeLib/ViewModel/Map/TestMapPageViewModel.cs +++ b/TestShareeLib/ViewModel/Map/TestMapPageViewModel.cs @@ -614,7 +614,7 @@ namespace TestShareeLib.UseCases.Startup // Add a reserved bike to station "FR101" new BikeInfo( new Bike("Id1", LockModel.ILockIt), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 123, // Lock id new Guid(), @@ -653,7 +653,7 @@ namespace TestShareeLib.UseCases.Startup // Add a reserved bike to station "FR101" new BikeInfo( new Bike("Id1", LockModel.ILockIt), - new Drive(), + new DriveMutable(), TINK.Model.Bikes.BikeInfoNS.BC.DataSource.Copri, 123, // Lock id new Guid(),