diff --git a/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml b/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml index f7ccd2a..1125868 100644 --- a/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml +++ b/LastenradBayern/TINK.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@ - - + + @@ -18,5 +18,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LastenradBayern/TINK.iOS/Info.plist b/LastenradBayern/TINK.iOS/Info.plist index 2405f7b..e58c8e9 100644 --- a/LastenradBayern/TINK.iOS/Info.plist +++ b/LastenradBayern/TINK.iOS/Info.plist @@ -1,4 +1,4 @@ - + @@ -24,6 +24,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + LSApplicationQueriesSchemes + + mailto + UILaunchStoryboardName LaunchScreen XSLaunchImageAssets @@ -49,8 +53,8 @@ CFBundleDisplayName LastenradBayern CFBundleVersion - 275 + 276 CFBundleShortVersionString - 3.0.275 + 3.0.276 diff --git a/LastenradBayern/TINK/View/Bike/ILockItBike.xaml b/LastenradBayern/TINK/View/Bike/ILockItBike.xaml index f7f1f83..df61bde 100644 --- a/LastenradBayern/TINK/View/Bike/ILockItBike.xaml +++ b/LastenradBayern/TINK/View/Bike/ILockItBike.xaml @@ -102,7 +102,11 @@ Text="{Binding TariffDescription.OperatorAgb}" IsVisible="{Binding TariffDescription.OperatorAgb, Converter={StaticResource Label_Converter}}" Grid.Row="5" - Grid.ColumnSpan="3"/> + Grid.ColumnSpan="3"> + + + + diff --git a/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs b/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs index 5f10035..3c198fc 100644 --- a/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs +++ b/LastenradBayern/TINK/View/FindBike/FindBikePage.xaml.cs @@ -2,6 +2,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using TINK.Model.Device; using TINK.ViewModel.FindBike; using Xamarin.CommunityToolkit.Extensions; using Xamarin.Forms; @@ -51,7 +52,8 @@ namespace TINK.View.FindBike model.Polling, (d, obj) => synchronizationContext.Post(d, obj), model.SmartDevice, - this) + this, + (url) => DependencyService.Get().OpenUrl(url)) { IsReportLevelVerbose = model.IsReportLevelVerbose }; diff --git a/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs b/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs index 38e220b..4fc0598 100644 --- a/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs +++ b/LastenradBayern/TINK/View/MyBikes/MyBikesPage.xaml.cs @@ -11,6 +11,7 @@ using Xamarin.Forms.Xaml; namespace TINK.View.MyBikes { using Serilog; + using TINK.Model.Device; using TINK.ViewModel.MyBikes; using Xamarin.CommunityToolkit.Extensions; @@ -68,7 +69,8 @@ namespace TINK.View.MyBikes model.Polling, (d, obj) => synchronizationContext.Post(d, obj), model.SmartDevice, - this) + this, + (url) => DependencyService.Get().OpenUrl(url)) { IsReportLevelVerbose = model.IsReportLevelVerbose }; diff --git a/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml b/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml index c43a5f5..d59defa 100644 --- a/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml +++ b/Meinkonrad/TINK.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@ - - + + @@ -18,5 +18,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meinkonrad/TINK.iOS/Info.plist b/Meinkonrad/TINK.iOS/Info.plist index c285a47..73dffbe 100644 --- a/Meinkonrad/TINK.iOS/Info.plist +++ b/Meinkonrad/TINK.iOS/Info.plist @@ -1,4 +1,4 @@ - + @@ -24,6 +24,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + LSApplicationQueriesSchemes + + mailto + UILaunchStoryboardName LaunchScreen XSLaunchImageAssets @@ -49,8 +53,8 @@ CFBundleDisplayName Mein konrad CFBundleVersion - 275 + 276 CFBundleShortVersionString - 3.0.275 + 3.0.276 diff --git a/Meinkonrad/TINK/View/Bike/ILockItBike.xaml b/Meinkonrad/TINK/View/Bike/ILockItBike.xaml index f7f1f83..df61bde 100644 --- a/Meinkonrad/TINK/View/Bike/ILockItBike.xaml +++ b/Meinkonrad/TINK/View/Bike/ILockItBike.xaml @@ -102,7 +102,11 @@ Text="{Binding TariffDescription.OperatorAgb}" IsVisible="{Binding TariffDescription.OperatorAgb, Converter={StaticResource Label_Converter}}" Grid.Row="5" - Grid.ColumnSpan="3"/> + Grid.ColumnSpan="3"> + + + + diff --git a/Meinkonrad/TINK/View/FindBike/FindBikePage.xaml.cs b/Meinkonrad/TINK/View/FindBike/FindBikePage.xaml.cs index 91ce74c..39b2afb 100644 --- a/Meinkonrad/TINK/View/FindBike/FindBikePage.xaml.cs +++ b/Meinkonrad/TINK/View/FindBike/FindBikePage.xaml.cs @@ -2,6 +2,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using TINK.Model.Device; using TINK.ViewModel.FindBike; using Xamarin.CommunityToolkit.Extensions; using Xamarin.Forms; @@ -51,7 +52,8 @@ namespace TINK.View.FindBike model.Polling, (d, obj) => synchronizationContext.Post(d, obj), model.SmartDevice, - this) + this, + (url) => DependencyService.Get().OpenUrl(url)) { IsReportLevelVerbose = model.IsReportLevelVerbose }; diff --git a/Meinkonrad/TINK/View/MyBikes/MyBikesPage.xaml.cs b/Meinkonrad/TINK/View/MyBikes/MyBikesPage.xaml.cs index 38e220b..4fc0598 100644 --- a/Meinkonrad/TINK/View/MyBikes/MyBikesPage.xaml.cs +++ b/Meinkonrad/TINK/View/MyBikes/MyBikesPage.xaml.cs @@ -11,6 +11,7 @@ using Xamarin.Forms.Xaml; namespace TINK.View.MyBikes { using Serilog; + using TINK.Model.Device; using TINK.ViewModel.MyBikes; using Xamarin.CommunityToolkit.Extensions; @@ -68,7 +69,8 @@ namespace TINK.View.MyBikes model.Polling, (d, obj) => synchronizationContext.Post(d, obj), model.SmartDevice, - this) + this, + (url) => DependencyService.Get().OpenUrl(url)) { IsReportLevelVerbose = model.IsReportLevelVerbose }; diff --git a/TINK/TINK.Android/Properties/AndroidManifest.xml b/TINK/TINK.Android/Properties/AndroidManifest.xml index bbaee90..9f8d0fd 100644 --- a/TINK/TINK.Android/Properties/AndroidManifest.xml +++ b/TINK/TINK.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@ - - + + @@ -18,5 +18,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TINK/TINK.iOS/Info.plist b/TINK/TINK.iOS/Info.plist index 082fdeb..d489f91 100644 --- a/TINK/TINK.iOS/Info.plist +++ b/TINK/TINK.iOS/Info.plist @@ -24,6 +24,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + LSApplicationQueriesSchemes + + mailto + UILaunchStoryboardName LaunchScreen XSLaunchImageAssets @@ -49,8 +53,8 @@ CFBundleDisplayName sharee.bike CFBundleVersion - 275 + 276 CFBundleShortVersionString - 3.0.275 + 3.0.276 diff --git a/TINK/TINK/View/Bike/ILockItBike.xaml b/TINK/TINK/View/Bike/ILockItBike.xaml index f7f1f83..df61bde 100644 --- a/TINK/TINK/View/Bike/ILockItBike.xaml +++ b/TINK/TINK/View/Bike/ILockItBike.xaml @@ -102,7 +102,11 @@ Text="{Binding TariffDescription.OperatorAgb}" IsVisible="{Binding TariffDescription.OperatorAgb, Converter={StaticResource Label_Converter}}" Grid.Row="5" - Grid.ColumnSpan="3"/> + Grid.ColumnSpan="3"> + + + + diff --git a/TINK/TINK/View/FindBike/FindBikePage.xaml.cs b/TINK/TINK/View/FindBike/FindBikePage.xaml.cs index 5f10035..3c198fc 100644 --- a/TINK/TINK/View/FindBike/FindBikePage.xaml.cs +++ b/TINK/TINK/View/FindBike/FindBikePage.xaml.cs @@ -2,6 +2,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using TINK.Model.Device; using TINK.ViewModel.FindBike; using Xamarin.CommunityToolkit.Extensions; using Xamarin.Forms; @@ -51,7 +52,8 @@ namespace TINK.View.FindBike model.Polling, (d, obj) => synchronizationContext.Post(d, obj), model.SmartDevice, - this) + this, + (url) => DependencyService.Get().OpenUrl(url)) { IsReportLevelVerbose = model.IsReportLevelVerbose }; diff --git a/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs b/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs index 38e220b..4fc0598 100644 --- a/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs +++ b/TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs @@ -11,6 +11,7 @@ using Xamarin.Forms.Xaml; namespace TINK.View.MyBikes { using Serilog; + using TINK.Model.Device; using TINK.ViewModel.MyBikes; using Xamarin.CommunityToolkit.Extensions; @@ -68,7 +69,8 @@ namespace TINK.View.MyBikes model.Polling, (d, obj) => synchronizationContext.Post(d, obj), model.SmartDevice, - this) + this, + (url) => DependencyService.Get().OpenUrl(url)) { IsReportLevelVerbose = model.IsReportLevelVerbose }; diff --git a/TINKLib/Model/WhatsNew.cs b/TINKLib/Model/WhatsNew.cs index 2695631..2ef5d5e 100644 --- a/TINKLib/Model/WhatsNew.cs +++ b/TINKLib/Model/WhatsNew.cs @@ -476,8 +476,8 @@ namespace TINK.Model AppResources.ChangeLog3_0_266 }, { - new Version(3, 0, 274), - AppResources.ChangeLog3_0_231 // Minor improvements. + new Version(3, 0, 276), + AppResources.ChangeLog3_0_276 } }; diff --git a/TINKLib/MultilingualResources/AppResources.Designer.cs b/TINKLib/MultilingualResources/AppResources.Designer.cs index 57896ef..a6c3a71 100644 --- a/TINKLib/MultilingualResources/AppResources.Designer.cs +++ b/TINKLib/MultilingualResources/AppResources.Designer.cs @@ -909,6 +909,17 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Link to operator AGB is displayed if required. + ///Display of status information extended. + ///Bugfix: Sending support mails works again. . + /// + public static string ChangeLog3_0_276 { + get { + return ResourceManager.GetString("ChangeLog3_0_276", resourceCulture); + } + } + /// /// Looks up a localized string similar to Lock of rented bike can not be found.. /// @@ -1818,6 +1829,16 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to An error occurred switching from cargo bikes/ city bikes. + ///{0}. + /// + public static string MessageMapPageErrorSwitch { + get { + return ResourceManager.GetString("MessageMapPageErrorSwitch", resourceCulture); + } + } + /// /// Looks up a localized string similar to Urgent questions?. /// diff --git a/TINKLib/MultilingualResources/AppResources.de.resx b/TINKLib/MultilingualResources/AppResources.de.resx index 4066a7b..187dfff 100644 --- a/TINKLib/MultilingualResources/AppResources.de.resx +++ b/TINKLib/MultilingualResources/AppResources.de.resx @@ -733,4 +733,13 @@ Deep linking wird unterstützt. Abfrage Erlaubnis für Zugriff azf Standort verbessert. + + Link auf Betreiber-AGB wird bei Bedarf angezeigt. +Anzeige von Statusinformationen erweitert. +Fehlerbehebung: Supportmails können wieder verschickt werden. + + + Beim Umschalten zwischen Lasten-/ Stadträdern ist ein Fehler aufgetreten. +{0} + \ No newline at end of file diff --git a/TINKLib/MultilingualResources/AppResources.resx b/TINKLib/MultilingualResources/AppResources.resx index f242c71..4b6c2b8 100644 --- a/TINKLib/MultilingualResources/AppResources.resx +++ b/TINKLib/MultilingualResources/AppResources.resx @@ -828,4 +828,13 @@ App supports deep linking. Location permissions handling improved. + + Link to operator AGB is displayed if required. +Display of status information extended. +Bugfix: Sending support mails works again. + + + An error occurred switching from cargo bikes/ city bikes. +{0} + \ No newline at end of file diff --git a/TINKLib/MultilingualResources/TINKLib.de.xlf b/TINKLib/MultilingualResources/TINKLib.de.xlf index b01fb77..a1cc748 100644 --- a/TINKLib/MultilingualResources/TINKLib.de.xlf +++ b/TINKLib/MultilingualResources/TINKLib.de.xlf @@ -984,6 +984,20 @@ Deep linking wird unterstützt. Location permissions handling improved. Abfrage Erlaubnis für Zugriff azf Standort verbessert. + + Link to operator AGB is displayed if required. +Display of status information extended. +Bugfix: Sending support mails works again. + Link auf Betreiber-AGB wird bei Bedarf angezeigt. +Anzeige von Statusinformationen erweitert. +Fehlerbehebung: Supportmails können wieder verschickt werden. + + + An error occurred switching from cargo bikes/ city bikes. +{0} + Beim Umschalten zwischen Lasten-/ Stadträdern ist ein Fehler aufgetreten. +{0} + diff --git a/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs index 4838dd0..e9bae60 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs @@ -36,6 +36,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC /// Object holding logged in user or an empty user object. /// Provides in use state information. /// View model to be used for progress report and unlocking/ locking view. + /// Delegate to open browser. public BikeViewModel( Func isConnectedDelegate, Func connectorFactory, @@ -46,7 +47,8 @@ namespace TINK.ViewModel.Bikes.Bike.BC BikeInfoMutable selectedBike, IUser activeUser, IInUseStateInfoProvider stateInfoProvider, - IBikesViewModel bikesViewModel) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, activeUser, stateInfoProvider, bikesViewModel) + IBikesViewModel bikesViewModel, + Action openUrlInBrowser) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, activeUser, stateInfoProvider, bikesViewModel, openUrlInBrowser) { RequestHandler = activeUser.IsLoggedIn ? RequestHandlerFactory.Create( diff --git a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs index 4ca2734..252cd60 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs @@ -1,5 +1,7 @@ -using System; +using Serilog; +using System; using System.ComponentModel; +using System.Text.RegularExpressions; using TINK.Model.Connector; using TINK.Model.Device; using TINK.Model.State; @@ -62,6 +64,9 @@ namespace TINK.ViewModel.Bikes.Bike /// View model to be used for progress report and unlocking/ locking view. protected IBikesViewModel BikesViewModel { get; } + /// Delegate to open browser. + private Action OpenUrlInBrowser; + /// /// Notifies GUI about changes. /// @@ -83,6 +88,7 @@ namespace TINK.ViewModel.Bikes.Bike /// Object holding logged in user or an empty user object. /// Provides in use state information. /// View model to be used for progress report and unlocking/ locking view. + /// Delegate to open browser. public BikeViewModelBase( Func isConnectedDelegate, Func connectorFactory, @@ -93,7 +99,8 @@ namespace TINK.ViewModel.Bikes.Bike BikeInfoMutable selectedBike, IUser activeUser, IInUseStateInfoProvider stateInfoProvider, - IBikesViewModel bikesViewModel) + IBikesViewModel bikesViewModel, + Action openUrlInBrowser) { IsConnectedDelegate = isConnectedDelegate; @@ -121,6 +128,8 @@ namespace TINK.ViewModel.Bikes.Bike 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}."); }); } /// @@ -309,5 +318,63 @@ namespace TINK.ViewModel.Bikes.Bike /// Gets the value of property when PropertyChanged was fired. public Color LastStateColor { get; set; } + /// Command object to bind login page redirect link to view model. + public System.Windows.Input.ICommand ShowAgbTappedCommand +#if USEFLYOUT + => new Xamarin.Forms.Command(() => ShowAgbPageAsync()); +#else + => new Xamarin.Forms.Command(async () => await OpenLoginPageAsync()); +#endif + + /// Opens login page. +#if USEFLYOUT + public void ShowAgbPageAsync() +#else + public async Task ShowAgbPageAsync() +#endif + { + try + { + // Switch to map page + +#if USEFLYOUT + var url = GetUrlFirstOrDefault(TariffDescription.OperatorAgb); + if (string.IsNullOrEmpty(url)) + { + // No url contained in string. + return; + } + + OpenUrlInBrowser(url); +#endif + } + catch (Exception p_oException) + { + Log.Error("An unexpected error occurred opening broser. {@Exception}", p_oException); + return; + } + } + + /// Gets first url from text. + /// url to extract text from. + /// Gets first url or an empty string if on url is contained in text. + + public static string GetUrlFirstOrDefault(string htmlSource) + { + if (string.IsNullOrEmpty(htmlSource)) + return string.Empty; + + try + { + var matches = new Regex(@"https://[-a-zA-Z0-9+&@#/%?=~_|!:, .;]*[-a-zA-Z0-9+&@#/%=~_|]").Matches(htmlSource); + return matches.Count > 0 + ? matches[0].Value + : string.Empty; + } catch (Exception e) + { + Log.ForContext().Error("Extracting URL failed. {Exception}", e); + return string.Empty; + } + } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs index 546e8be..bd3b3f8 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs @@ -13,6 +13,7 @@ namespace TINK.ViewModel.Bikes.Bike /// Provides info about the smart device (phone, tablet, ...). /// Provides in use state information. /// View model to be used for progress report and unlocking/ locking view. + /// Delegate to open browser. public static BikeViewModelBase Create( Func isConnectedDelegate, Func connectorFactory, @@ -25,7 +26,8 @@ namespace TINK.ViewModel.Bikes.Bike Model.Bike.BC.BikeInfoMutable bikeInfo, IUser activeUser, IInUseStateInfoProvider stateInfoProvider, - IBikesViewModel bikesViewModel) + IBikesViewModel bikesViewModel, + Action openUrlInBrowser) { return bikeInfo as Model.Bikes.Bike.BluetoothLock.IBikeInfoMutable != null ? new BluetoothLock.BikeViewModel( @@ -40,7 +42,8 @@ namespace TINK.ViewModel.Bikes.Bike bikeInfo as Model.Bike.BluetoothLock.BikeInfoMutable, activeUser, stateInfoProvider, - bikesViewModel) as BikeViewModelBase + bikesViewModel, + openUrlInBrowser) as BikeViewModelBase : new BC.BikeViewModel( isConnectedDelegate, connectorFactory, @@ -51,7 +54,8 @@ namespace TINK.ViewModel.Bikes.Bike bikeInfo, activeUser, stateInfoProvider, - bikesViewModel); + bikesViewModel, + openUrlInBrowser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs index 923fd7a..f6ffe2f 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs @@ -83,6 +83,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock /// Object holding logged in user or an empty user object. /// Provides in use state information. /// View model to be used for progress report and unlocking/ locking view. + /// Delegate to open browser. public BikeViewModel( Func isConnectedDelegate, Func connectorFactory, @@ -95,7 +96,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock BikeInfoMutable selectedBike, IUser user, IInUseStateInfoProvider stateInfoProvider, - IBikesViewModel bikesViewModel) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, user, stateInfoProvider, bikesViewModel) + IBikesViewModel bikesViewModel, + Action openUrlInBrowser) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, user, stateInfoProvider, bikesViewModel, openUrlInBrowser) { RequestHandler = user.IsLoggedIn ? RequestHandlerFactory.Create( diff --git a/TINKLib/ViewModel/Bikes/BikesViewModel.cs b/TINKLib/ViewModel/Bikes/BikesViewModel.cs index fbeb06c..bcd2f61 100644 --- a/TINKLib/ViewModel/Bikes/BikesViewModel.cs +++ b/TINKLib/ViewModel/Bikes/BikesViewModel.cs @@ -54,6 +54,9 @@ namespace TINK.ViewModel.Bikes /// Action to post to GUI thread. public Action PostAction { get; } + /// Delegate to open browser. + private Action OpenUrlInBrowser { get; } + /// Enables derived class to fire property changed event. /// protected override void OnPropertyChanged(PropertyChangedEventArgs p_oEventArgs) => base.OnPropertyChanged(p_oEventArgs); @@ -89,6 +92,7 @@ namespace TINK.ViewModel.Bikes /// Executes actions on GUI thread. /// Provides info about the smart device (phone, tablet, ...) /// Interface to actuate methodes on GUI. + /// Delegate to open browser. public BikesViewModel( User user, ILocationPermission permissions, @@ -102,6 +106,7 @@ namespace TINK.ViewModel.Bikes Action postAction, ISmartDevice smartDevice, IViewService viewService, + Action openUrlInBrowser, Func itemFactory) { User = user @@ -150,6 +155,8 @@ namespace TINK.ViewModel.Bikes isConnected = IsConnectedDelegate(); + OpenUrlInBrowser = openUrlInBrowser; + CollectionChanged += (sender, eventargs) => { // Notify about bikes occuring/ vanishing from list. @@ -186,7 +193,8 @@ namespace TINK.ViewModel.Bikes l_oBike, User, m_oItemFactory(), - this); + this, + OpenUrlInBrowser); bikeViewModel.PropertyChanged += OnBikeRequestHandlerPropertyChanged; diff --git a/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs b/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs index b69bdb5..7a096c9 100644 --- a/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs +++ b/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs @@ -36,9 +36,6 @@ namespace TINK.ViewModel.BikesAtStation /// private readonly IStation m_oStation; - /// Holds a reference to the external trigger service. - private Action OpenUrlInExternalBrowser { get; } - /// /// Constructs bike collection view model. /// @@ -70,11 +67,8 @@ namespace TINK.ViewModel.BikesAtStation Action openUrlInExternalBrowser, Action postAction, ISmartDevice smartDevice, - IViewService viewService) : base(user, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, smartDevice, viewService, () => new BikeAtStationInUseStateInfoProvider()) + IViewService viewService) : base(user, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, smartDevice, viewService, openUrlInExternalBrowser, () => new BikeAtStationInUseStateInfoProvider()) { - OpenUrlInExternalBrowser = openUrlInExternalBrowser - ?? throw new ArgumentException("Can not instantiate login page view model- object. No user external browse service available."); - m_oStation = selectedStation; Title = string.Format(m_oStation?.StationName != null @@ -387,20 +381,5 @@ namespace TINK.ViewModel.BikesAtStation base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(NoBikesAtStationText))); } } - - /// Opens login page. - /// Url to open. - private void RegisterRequest(string url) - { - try - { - OpenUrlInExternalBrowser(url); - } - catch (Exception p_oException) - { - Log.Error("Ein unerwarteter Fehler ist auf der Login Seite beim Öffnen eines Browsers, Seite {url}, aufgetreten. {@Exception}", url, p_oException); - return; - } - } } } \ No newline at end of file diff --git a/TINKLib/ViewModel/FindBike/FindBikePageViewModel.cs b/TINKLib/ViewModel/FindBike/FindBikePageViewModel.cs index ba60c61..41094f3 100644 --- a/TINKLib/ViewModel/FindBike/FindBikePageViewModel.cs +++ b/TINKLib/ViewModel/FindBike/FindBikePageViewModel.cs @@ -75,6 +75,7 @@ namespace TINK.ViewModel.FindBike /// Executes actions on GUI thread. /// Provides info about the smart device (phone, tablet, ...). /// Interface to actuate methodes on GUI. + /// Delegate to open browser. public FindBikePageViewModel( User p_oUser, ILocationPermission permissions, @@ -88,7 +89,8 @@ namespace TINK.ViewModel.FindBike PollingParameters polling, Action postAction, ISmartDevice smartDevice, - IViewService viewService) : base(p_oUser, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, smartDevice, viewService, () => new MyBikeInUseStateInfoProvider()) + IViewService viewService, + Action openUrlInBrowser) : base(p_oUser, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, smartDevice, viewService, openUrlInBrowser, () => new MyBikeInUseStateInfoProvider()) { CollectionChanged += (sender, eventargs) => { diff --git a/TINKLib/ViewModel/Map/MapPageViewModel.cs b/TINKLib/ViewModel/Map/MapPageViewModel.cs index bbc236b..e644bd5 100644 --- a/TINKLib/ViewModel/Map/MapPageViewModel.cs +++ b/TINKLib/ViewModel/Map/MapPageViewModel.cs @@ -333,7 +333,7 @@ namespace TINK.ViewModel.Map await ViewService.DisplayAlert( "Information", resultStationsAndBikes.GeneralData.MerchantMessage, - "OK"); + AppResources.MessageAnswerOk); WasMerchantMessageAlreadyShown = true; } @@ -876,9 +876,13 @@ namespace TINK.ViewModel.Map { try { + IsMapPageEnabled = false; + IsRunning = true; + Log.ForContext().Information($"Request to toggle to \"{p_strSelectedFilter}\"."); // Stop polling. + ActionText = AppResources.ActivityTextOneMomentPlease; await m_oViewUpdateManager.StopUpdatePeridically(); // Clear error info. @@ -896,27 +900,29 @@ namespace TINK.ViewModel.Map Pins.Clear(); // Check location permission + ActionText = AppResources.ActivityTextRequestingLocationPermissions; + var status = await PermissionsService.CheckStatusAsync(); if (TinkApp.CenterMapToCurrentLocation && !GeolocationService.IsSimulation && status != Status.Granted) { - var permissionResult = await PermissionsService.RequestAsync(); + status = await PermissionsService.RequestAsync(); - if (permissionResult != Status.Granted) + if (status != Status.Granted) { var dialogResult = await ViewService.DisplayAlert( - AppResources.MessageTitleHint, - AppResources.MessageBikesManagementLocationPermission, - "Ja", - "Nein"); - + AppResources.MessageTitleHint, + AppResources.MessageCenterMapLocationPermissionOpenDialog, + AppResources.MessageAnswerYes, + AppResources.MessageAnswerNo); if (dialogResult) { // User decided to give access to locations permissions. PermissionsService.OpenAppSettings(); - IsMapPageEnabled = true; ActionText = ""; + IsRunning = false; + IsMapPageEnabled = true; return; } } @@ -930,13 +936,16 @@ namespace TINK.ViewModel.Map AppResources.MessageTitleHint, AppResources.MessageBikesManagementBluetoothActivation, AppResources.MessageAnswerOk); - IsMapPageEnabled = true; ActionText = ""; + IsRunning = false; + IsMapPageEnabled = true; return; } } // Move and scale before getting stations and bikes which takes some time. + ActionText = AppResources.ActivityTextCenterMap; + if (TinkApp.CenterMapToCurrentLocation) { Location currentLocation = null; @@ -962,6 +971,7 @@ namespace TINK.ViewModel.Map // Update stations MoveAndScale(m_oMoveToRegionDelegate, TinkApp.ActiveMapSpan); + ActionText = AppResources.ActivityTextMapLoadingStationsAndBikes; IsConnected = TinkApp.GetIsConnected(); var resultStationsAndBikes = await TinkApp.GetConnector(IsConnected).Query.GetBikesAndStationsAsync(); @@ -990,16 +1000,24 @@ namespace TINK.ViewModel.Map // Excpetions are handled insde update task; } + ActionText = ""; + IsRunning = false; + IsMapPageEnabled = true; Log.ForContext().Information($"Toggle to \"{p_strSelectedFilter}\" done."); } catch (Exception l_oException) { Log.ForContext().Error("An error occurred switching view Cargobike/ Citybike.{}"); + ActionText = ""; + IsRunning = false; await ViewService.DisplayAlert( "Fehler", - $"Beim Umschalten TINK/ Konrad ist ein Fehler aufgetreten.\r\n{l_oException.Message}", - "OK"); + AppResources.MessageMapPageErrorSwitch, + String.Format(AppResources.MessageMapPageErrorSwitch, l_oException.Message), + AppResources.MessageAnswerOk); + + IsMapPageEnabled = true; } } diff --git a/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs b/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs index 31275fb..d78fff8 100644 --- a/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs +++ b/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs @@ -47,6 +47,7 @@ namespace TINK.ViewModel.MyBikes /// Executes actions on GUI thread. /// Provides info about the smart device (phone, tablet, ...). /// Interface to actuate methodes on GUI. + /// Delegate to open browser. public MyBikesPageViewModel( User p_oUser, ILocationPermission permissions, @@ -60,7 +61,8 @@ namespace TINK.ViewModel.MyBikes PollingParameters p_oPolling, Action postAction, ISmartDevice smartDevice, - IViewService viewService) : base(p_oUser, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, p_oPolling, postAction, smartDevice, viewService, () => new MyBikeInUseStateInfoProvider()) + IViewService viewService, + Action openUrlInBrowser) : base(p_oUser, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, p_oPolling, postAction, smartDevice, viewService, openUrlInBrowser, () => new MyBikeInUseStateInfoProvider()) { CollectionChanged += (sender, eventargs) => { diff --git a/TestShareeLib/ViewModel/Bikes/Bike/TestBikeViewModelBase.cs b/TestShareeLib/ViewModel/Bikes/Bike/TestBikeViewModelBase.cs new file mode 100644 index 0000000..9053f60 --- /dev/null +++ b/TestShareeLib/ViewModel/Bikes/Bike/TestBikeViewModelBase.cs @@ -0,0 +1,33 @@ +using NUnit.Framework; +using TINK.ViewModel.Bikes.Bike; + +namespace TestShareeLib.ViewModel.Bikes.Bike +{ + [TestFixture] + public class TestBikeViewModelBase + { + [Test] + public void TestGetUrl() + { + Assert.That( + BikeViewModelBase.GetUrlFirstOrDefault(@"Mit der Mietrad Anmietung wird folgender Betreiber AGB zugestimmt (als Demo sharee AGB)."), + Is.EqualTo(@"https://shareeapp-fr01.copri.eu/site/agb.html")); + } + + [Test] + public void TestGetUrlNoProtocol() + { + Assert.That( + BikeViewModelBase.GetUrlFirstOrDefault(@"Mit der Mietrad Anmietung wird folgender Betreiber AGB zugestimmt (als Demo sharee AGB)."), + Is.EqualTo("")); + } + + [Test] + public void TestGetUrlNull() + { + Assert.That( + BikeViewModelBase.GetUrlFirstOrDefault(null), + Is.EqualTo("")); + } + } +} diff --git a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeAtStationViewModel.cs b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeAtStationViewModel.cs index 0f2785a..35d49b0 100644 --- a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeAtStationViewModel.cs +++ b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeAtStationViewModel.cs @@ -68,7 +68,8 @@ namespace UITest.Fixtures.ViewModel l_oBike, l_oUser, new MyBikeInUseStateInfoProvider(), - MockRepository.GenerateStub()); + MockRepository.GenerateStub(), + url => { }); Assert.AreEqual("2", l_oViewModel.Name); Assert.AreEqual("", l_oViewModel.DisplayId); @@ -94,7 +95,8 @@ namespace UITest.Fixtures.ViewModel bike, user, new BikeAtStationInUseStateInfoProvider(), - MockRepository.GenerateStub())); + MockRepository.GenerateStub(), + url => { })); Assert.AreEqual("Still 15 minutes reserved.", l_oViewModel.StateText); } @@ -116,7 +118,8 @@ namespace UITest.Fixtures.ViewModel bike, user, new BikeAtStationInUseStateInfoProvider(), - MockRepository.GenerateStub())); + MockRepository.GenerateStub(), + url => { })); Assert.AreEqual("Code 4asdfA, still 7 minutes reserved.", l_oViewModel.StateText); } @@ -138,7 +141,8 @@ namespace UITest.Fixtures.ViewModel bike, user, new BikeAtStationInUseStateInfoProvider(), - MockRepository.GenerateStub())); + MockRepository.GenerateStub(), + url => { })); Assert.AreEqual( $"Code 4asdfA, rented since {new DateTime(2018, 10, 24, 21, 49, 00).ToString("dd. MMMM HH:mm")}.", @@ -174,7 +178,8 @@ namespace UITest.Fixtures.ViewModel l_oStoreMock.Load().Result, "123456789"), // Device id new BikeAtStationInUseStateInfoProvider(), - MockRepository.GenerateStub()); + MockRepository.GenerateStub(), + url => { }); Assert.AreEqual("Test description", l_oViewModel.Name); Assert.AreEqual("2", l_oViewModel.DisplayId); @@ -212,7 +217,8 @@ namespace UITest.Fixtures.ViewModel l_oStoreMock.Load().Result, "123456789"), new BikeAtStationInUseStateInfoProvider(), - MockRepository.GenerateStub()); + MockRepository.GenerateStub(), + url => { }); Assert.AreEqual("Test description", l_oViewModel.Name); Assert.AreEqual("2", l_oViewModel.DisplayId); diff --git a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeViewModelFactory.cs b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeViewModelFactory.cs index cd9f498..963becc 100644 --- a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeViewModelFactory.cs +++ b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestBikeViewModelFactory.cs @@ -31,7 +31,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel new TINK.Model.Bike.BC.BikeInfoMutable(new TINK.Model.Bike.BluetoothLock.BikeInfo("42", 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "42"), "My Station Name"), MockRepository.GenerateStub(), // user MockRepository.GenerateStub(), - MockRepository.GenerateStub()).GetType()); // stateInfoProvider + MockRepository.GenerateStub(), + url => { }).GetType()); // stateInfoProvider Assert.AreEqual( typeof(TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel), @@ -47,7 +48,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel new TINK.Model.Bike.BluetoothLock.BikeInfoMutable(new TINK.Model.Bike.BluetoothLock.BikeInfo("42", 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "42"), "My Station Name"), MockRepository.GenerateStub(), // user MockRepository.GenerateStub(), - MockRepository.GenerateStub()).GetType()); // stateInfoProvider + MockRepository.GenerateStub(), + url => { }).GetType()); // stateInfoProvider } } } diff --git a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikePageViewModel.cs b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikePageViewModel.cs index b3a5307..bf02c58 100644 --- a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikePageViewModel.cs +++ b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikePageViewModel.cs @@ -28,7 +28,8 @@ namespace UITest.Fixtures.ViewModel bike, user, new MyBikeInUseStateInfoProvider(), - MockRepository.GenerateStub())); + MockRepository.GenerateStub(), + url => { })); Assert.AreEqual("Location Station 3, still 15 minutes reserved.", l_oViewModel.StateText); @@ -51,7 +52,8 @@ namespace UITest.Fixtures.ViewModel bike, user, new MyBikeInUseStateInfoProvider(), - MockRepository.GenerateStub())); + MockRepository.GenerateStub(), + url => { })); Assert.AreEqual("Code 4asdfA, location Station 3, still 7 minutes reserved.", l_oViewModel.StateText); } @@ -75,7 +77,8 @@ namespace UITest.Fixtures.ViewModel bike, user, new MyBikeInUseStateInfoProvider(), - MockRepository.GenerateStub())); + MockRepository.GenerateStub(), + url => { })); Assert.AreEqual( $"Code 4asdfA, location Station 3, rented since {new DateTime(2018, 10, 24, 21, 49, 00).ToString("dd. MMMM HH:mm")}.", diff --git a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikesPageViewModel.cs b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikesPageViewModel.cs index 5f03959..f110601 100644 --- a/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikesPageViewModel.cs +++ b/TestTINKLib/Fixtures/ObjectTests/ViewModel/TestMyBikesPageViewModel.cs @@ -98,7 +98,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService); + viewService, + url => { }); await myBikes.OnAppearing(); @@ -191,7 +192,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService); + viewService, + url => { }); await myBikes.OnAppearing(); @@ -292,7 +294,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService); + viewService, + url => { }); await myBikes.OnAppearing(); @@ -388,7 +391,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService); + viewService, + url => { }); await myBikes.OnAppearing(); @@ -482,7 +486,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService); + viewService, + url => { }); await myBikes.OnAppearing(); @@ -570,7 +575,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService); + viewService, + url => { }); await myBikes.OnAppearing(); @@ -633,7 +639,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService); + viewService, + url => { }); await myBikes.OnAppearing(); @@ -710,7 +717,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService) + viewService, + url => { }) { IsReportLevelVerbose = true }; @@ -790,7 +798,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), - viewService) + viewService, + url => { }) { IsReportLevelVerbose = true };