mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-12-22 06:56:25 +01:00
Closing lock and returning bike speeded up.
This commit is contained in:
parent
e4adeb908c
commit
db9c288584
70 changed files with 933 additions and 902 deletions
5
TINK.sln
5
TINK.sln
|
@ -24,6 +24,11 @@ EndProject
|
|||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestLockItBLE", "TestLockItBLE\TestLockItBLE.csproj", "{2581E9AD-4F56-431A-AB87-1B6D80D546AA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
TINK\TINK\TINK.projitems*{5297504f-603f-4e1a-98aa-57c4a0d9d833}*SharedItemsImports = 13
|
||||
TINK\TINK\TINK.projitems*{62b8950a-70b8-4f9d-affc-0a1ebe7bc9e7}*SharedItemsImports = 4
|
||||
TINK\TINK\TINK.projitems*{f2d8208f-a8bf-4403-b0ae-2a1d270e4dc9}*SharedItemsImports = 4
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||
Ad-Hoc|ARM = Ad-Hoc|ARM
|
||||
|
|
|
@ -18,6 +18,6 @@ namespace TINK.Droid.Model.Device
|
|||
/// <summary> Gets unitque device identifier. </summary>
|
||||
/// <returns>Gets the identifies specifying device.</returns>
|
||||
public string Identifier
|
||||
=> Android.Provider.Settings.Secure.GetString(Forms.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
|
||||
=> Android.Provider.Settings.Secure.GetString(Android.App.Application.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ namespace TINK.Droid.Model.Device
|
|||
{
|
||||
get
|
||||
{
|
||||
LocationManager locationManager = (LocationManager)Forms.Context.GetSystemService(Context.LocationService);
|
||||
LocationManager locationManager = (LocationManager)Android.App.Application.Context.GetSystemService(Context.LocationService);
|
||||
return locationManager.IsProviderEnabled(LocationManager.GpsProvider);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.243" android:versionCode="243">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.244" android:versionCode="244">
|
||||
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="30" />
|
||||
<!-- Google Maps related permissions -->
|
||||
<permission android:name="com.ecs.google.maps.v2.actionbarsherlock.permission.MAPS_RECEIVE" android:protectionLevel="signature" />
|
||||
|
|
|
@ -32,25 +32,27 @@
|
|||
<string>TINK</string>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Media.xcassets/AppIcons.appiconset</string>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>Communicate with Bluetooth lock.</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>Communicate with Bluetooth lock.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Show map at current positon and pass positon to server when returning bike.</string>
|
||||
<!--Description of the Bluetooth request message (required on iOS 13)-->
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>Is required to communicate with Bluetooth lock.</string>
|
||||
<!--Description of the Bluetooth request message (required on iOS 10, deprecated)-->
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>Is required to communicate with Bluetooth lock.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Is required to show map at current position and pass position to server when returning bike.</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>Show map at current positon and pass positon to server when returning bike.</string>
|
||||
<string>Is required to show map at current position and pass position to server when returning bike.</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.TeilRad.sharee.bike</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>9.0</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Show map at current positon and pass positon to server when returning bike.</string>
|
||||
<string>Is required to show map at current position and pass position to server when returning bike.</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>sharee.bike</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>243</string>
|
||||
<string>244</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.241</string>
|
||||
<string>3.0.244</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -127,21 +127,7 @@ namespace TINK
|
|||
if (lastVersion > new Version(3, 0, 173))
|
||||
GeolocationServicesContainer.SetActive(settings.ActiveGeolocationService);
|
||||
|
||||
if (new Version(0, 0, 0) < lastVersion
|
||||
&& lastVersion <= new Version(3, 0, 234))
|
||||
{
|
||||
// Version 3.0.245 and older used Xamarin.Auth.AccountStore to securely store data.
|
||||
// Later version s use Xamarin.Essentials Secure Storage.
|
||||
store = new StoreLegacy(settings.ActiveUri.GetHashCode().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Either
|
||||
// - frist install or
|
||||
// - version whitch uses secure storage
|
||||
// detected.
|
||||
store = new Store();
|
||||
}
|
||||
store = new Store();
|
||||
|
||||
Barrel.ApplicationId = "TINKApp";
|
||||
|
||||
|
@ -184,12 +170,7 @@ namespace TINK
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
#if USEMASTERDETAIL
|
||||
// Use master detail page.
|
||||
MainPage = ModelRoot.WhatsNew.IsShowRequired
|
||||
? new View.WhatsNew.WhatsNewPage(() => MainPage = new View.MainPage()) // Show whats new info.
|
||||
: (Page) new View.MainPage(); // Just use TINKApp
|
||||
#elif USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
// Use flyout page.
|
||||
MainPage = ModelRoot.WhatsNew.IsShowRequired
|
||||
? new View.WhatsNew.WhatsNewPage(() => MainPage = new View.Root.RootPage()) // Show whats new info.
|
||||
|
|
|
@ -26,7 +26,12 @@ namespace TINK
|
|||
/// <returns></returns>
|
||||
static Page GetCurrentPage()
|
||||
{
|
||||
return (Application.Current.MainPage as MasterDetailPage)?.Detail.Navigation.NavigationStack.LastOrDefault();
|
||||
|
||||
#if USEFLYOUT
|
||||
return (Application.Current.MainPage as FlyoutPage)?.Detail.Navigation.NavigationStack.LastOrDefault();
|
||||
#else
|
||||
return (Application.Current.MainPage as AppShellViewModel)?.Detail.Navigation.NavigationStack.LastOrDefault();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
|
||||
namespace TINK.Model.User.Account
|
||||
{
|
||||
public class StoreLegacy : IStore
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the name of the application.
|
||||
/// </summary>
|
||||
private const string M_STR_APPNAME = "TINKApp";
|
||||
|
||||
/// <summary> Holds the id of the session. </summary>
|
||||
private const string KEY_SESSIONID = "SessionId";
|
||||
|
||||
/// <summary> Holds the id of the session. </summary>
|
||||
private const string KEY_GROUP = "Group";
|
||||
|
||||
/// <summary> Holds the id of the session. </summary>
|
||||
private const string KEY_DEBUGLEVEL = "DebugLevel";
|
||||
|
||||
public StoreLegacy(string copriHostHash)
|
||||
{
|
||||
m_strCopriHostHash = copriHostHash
|
||||
?? throw new ArgumentException("Can not construct account object. Copri hash must not be null.");
|
||||
}
|
||||
|
||||
private string m_strCopriHostHash;
|
||||
|
||||
/// <summary>
|
||||
/// Reads mail address and password from account store.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IAccount> Load()
|
||||
{
|
||||
#if !WINDOWS_UWP
|
||||
#if !__IOS__
|
||||
var xamAccountStore = Xamarin.Auth.AccountStore.Create("System.Char[]").FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
|
||||
#else
|
||||
var xamAccountStore = Xamarin.Auth.AccountStore.Create().FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
|
||||
#endif
|
||||
if (xamAccountStore == null)
|
||||
{
|
||||
// Nothing t do if account cannot be accessed.
|
||||
return new EmptyAccount();
|
||||
}
|
||||
|
||||
return new Account(
|
||||
xamAccountStore.Username,
|
||||
string.Empty,
|
||||
xamAccountStore.Properties.ContainsKey(KEY_SESSIONID) ? xamAccountStore.Properties[KEY_SESSIONID] : null,
|
||||
xamAccountStore.Properties.ContainsKey(KEY_GROUP) && !string.IsNullOrEmpty(xamAccountStore.Properties[KEY_GROUP]) ? JsonConvert.DeserializeObject<IEnumerable<string>>(xamAccountStore.Properties[KEY_GROUP]) : new string[0],
|
||||
xamAccountStore.Properties.ContainsKey(KEY_DEBUGLEVEL) && !string.IsNullOrEmpty(xamAccountStore.Properties[KEY_DEBUGLEVEL]) ? Permissions.Parse<Permissions>(xamAccountStore.Properties[KEY_DEBUGLEVEL]) : Permissions.None);
|
||||
#else
|
||||
return new Account(
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
new List<string>(),
|
||||
null);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes mail address and password to account store.
|
||||
/// </summary>
|
||||
/// <param name="account"></param>
|
||||
public async Task Save(IAccount account)
|
||||
{
|
||||
#if !WINDOWS_UWP
|
||||
Xamarin.Auth.Account xamAccount = new Xamarin.Auth.Account
|
||||
{
|
||||
Username = account.Mail
|
||||
};
|
||||
|
||||
xamAccount.Properties.Add(KEY_SESSIONID, account?.SessionCookie);
|
||||
xamAccount.Properties.Add(KEY_GROUP, JsonConvert.SerializeObject(account?.Group ?? new string[0]));
|
||||
xamAccount.Properties.Add(KEY_DEBUGLEVEL, account.DebugLevel.ToString());
|
||||
#if !__IOS__
|
||||
Xamarin.Auth.AccountStore.Create("System.Char[]").Save(xamAccount, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
|
||||
#else
|
||||
Xamarin.Auth.AccountStore.Create().Save(xamAccount, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes mail address and password from account store.
|
||||
/// </summary>
|
||||
public IAccount Delete(IAccount account)
|
||||
{
|
||||
#if !WINDOWS_UWP
|
||||
#if !__IOS__
|
||||
var xamAccountStore = Xamarin.Auth.AccountStore.Create("System.Char[]").FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
|
||||
#else
|
||||
var xamAccountStore = Xamarin.Auth.AccountStore.Create().FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
|
||||
#endif
|
||||
if (xamAccountStore == null)
|
||||
{
|
||||
return new EmptyAccount();
|
||||
}
|
||||
#if !__IOS__
|
||||
Xamarin.Auth.AccountStore.Create("System.Char[]").Delete(xamAccountStore, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
|
||||
#else
|
||||
Xamarin.Auth.AccountStore.Create().Delete(xamAccountStore, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
|
||||
#endif
|
||||
#endif
|
||||
return new EmptyAccount();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -99,10 +99,8 @@
|
|||
<DependentUpon>WhatsNewPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Model\User\Account\Store.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\ViewModelResourceHelper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\RootMasterDetail\Helper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\RootMasterDetail\MasterPageViewModel.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)View\BikesAtStation\BikesAtStationPage.xaml.cs">
|
||||
<DependentUpon>BikesAtStationPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
|
@ -123,14 +121,6 @@
|
|||
<DependentUpon>LoginPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MasterPage.xaml.cs">
|
||||
<DependentUpon>MasterPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MainPageMenuItem.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
@ -181,18 +171,6 @@
|
|||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MainPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MasterPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)View\Contact\ContactPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
using System.Threading.Tasks;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using System;
|
||||
|
@ -12,7 +12,7 @@ using TINK.ViewModel.Account;
|
|||
namespace TINK.View.Account
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class AccountPage : ContentPage, IViewService, IDetailPage
|
||||
#else
|
||||
public partial class AccountPage : ContentPage, IViewService
|
||||
|
@ -86,7 +86,7 @@ namespace TINK.View.Account
|
|||
public new async Task<string> DisplayActionSheet(String title, String cancel, String destruction, params String[] p_oButtons)
|
||||
=> await base.DisplayActionSheet(title, cancel, destruction, p_oButtons);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Creates and a page an shows it.
|
||||
/// </summary>
|
||||
|
@ -108,7 +108,7 @@ namespace TINK.View.Account
|
|||
public Task PopModalAsync()
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigation;
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace TINK.View.BikesAtStation
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.ViewModel;
|
||||
|
@ -25,7 +25,7 @@ using TINK.View.MasterDetail;
|
|||
using Xamarin.CommunityToolkit.Extensions;
|
||||
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class BikesAtStationPage : ContentPage, IViewService, IDetailPage
|
||||
#else
|
||||
public partial class BikesAtStationPage : ContentPage, IViewService
|
||||
|
@ -180,7 +180,7 @@ using TINK.View.MasterDetail;
|
|||
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
|
||||
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary> Creates and a page an shows it.</summary>
|
||||
/// <remarks> When user is not logged in navigation to Login page is supported.</remarks>
|
||||
/// <param name="p_oTypeOfPage">Type of page to show.</param>
|
||||
|
@ -209,7 +209,7 @@ using TINK.View.MasterDetail;
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Delegate to perform navigation.
|
||||
/// </summary>
|
||||
|
|
|
@ -47,8 +47,8 @@ namespace TINK.View.Contact
|
|||
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
public void ShowPage(ViewTypes p_oType, string title = null)
|
||||
=> NavigationMasterDetail.ShowPage(p_oType.GetViewType(), title);
|
||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
||||
=> throw new NotImplementedException();
|
||||
#else
|
||||
/// <summary> Shows a page.</summary>
|
||||
/// <param name="route">Route of the page to show.</param>
|
||||
|
@ -91,7 +91,7 @@ namespace TINK.View.Contact
|
|||
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
||||
#endif
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to perform navigation.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using Xamarin.Forms;
|
||||
|
@ -14,10 +14,10 @@ namespace TINK.View.Contact
|
|||
using TINK.ViewModel.Contact;
|
||||
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class SelectStationPage : ContentPage, IViewService, IDetailPage
|
||||
#else
|
||||
public partial class MapPage : ContentPage, IViewService
|
||||
public partial class SelectStationPage : ContentPage, IViewService
|
||||
#endif
|
||||
{
|
||||
/// <summary> View model to notify about whether page appears or hides. </summary>
|
||||
|
@ -70,7 +70,7 @@ namespace TINK.View.Contact
|
|||
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Creates and a page an shows it.
|
||||
/// </summary>
|
||||
|
@ -96,17 +96,17 @@ namespace TINK.View.Contact
|
|||
/// <param name="typeOfPage">Page to display.</param>
|
||||
public async Task PushAsync(ViewTypes typeOfPage)
|
||||
{
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
var page = Activator.CreateInstance(typeOfPage.GetViewType()) as IDetailPage;
|
||||
#else
|
||||
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
|
||||
var page = Activator.CreateInstance(typeOfPage.GetViewType());
|
||||
#endif
|
||||
if (page == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
page.NavigationMasterDetail = NavigationMasterDetail;
|
||||
#endif
|
||||
|
||||
|
@ -119,7 +119,7 @@ namespace TINK.View.Contact
|
|||
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
||||
#endif
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail { private get; set; }
|
||||
|
||||
|
@ -159,7 +159,7 @@ namespace TINK.View.Contact
|
|||
{
|
||||
BindingContext = SelectStationPageViewModel;
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
SelectStationPageViewModel.NavigationMasterDetail = NavigationMasterDetail;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ namespace TINK.View.FindBike
|
|||
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
||||
=> throw new NotImplementedException();
|
||||
#else
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.ViewModel;
|
||||
|
@ -11,7 +11,7 @@ using Xamarin.Forms.Xaml;
|
|||
namespace TINK.View.Info.BikeInfo
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class BikeInfoCarouselPage : CarouselPage, IViewService, IDetailPage
|
||||
#else
|
||||
public partial class BikeInfoCarouselPage : CarouselPage, IViewService
|
||||
|
@ -70,7 +70,7 @@ namespace TINK.View.Info.BikeInfo
|
|||
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Creates and a page an shows it.
|
||||
/// </summary>
|
||||
|
@ -104,7 +104,7 @@ namespace TINK.View.Info.BikeInfo
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Delegate to perform navigation.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.ViewModel;
|
||||
|
@ -11,7 +11,7 @@ using Xamarin.Forms.Xaml;
|
|||
namespace TINK.View.Login
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class LoginPage : ContentPage, IViewService, IDetailPage
|
||||
#else
|
||||
public partial class LoginPage : ContentPage, IViewService
|
||||
|
@ -65,7 +65,7 @@ namespace TINK.View.Login
|
|||
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Creates and a page an shows it.
|
||||
/// </summary>
|
||||
|
@ -98,7 +98,7 @@ namespace TINK.View.Login
|
|||
await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Delegate to perform navigation.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using Xamarin.Forms;
|
||||
|
@ -12,7 +12,7 @@ namespace TINK.View.Map
|
|||
using TINK.ViewModel.Map;
|
||||
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class MapPage : ContentPage, IViewService, IDetailPage
|
||||
#else
|
||||
public partial class MapPage : ContentPage, IViewService
|
||||
|
@ -71,7 +71,7 @@ namespace TINK.View.Map
|
|||
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Creates and a page an shows it.
|
||||
/// </summary>
|
||||
|
@ -97,7 +97,7 @@ namespace TINK.View.Map
|
|||
/// <param name="typeOfPage">Page to display.</param>
|
||||
public async Task PushAsync(ViewTypes typeOfPage)
|
||||
{
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
var page = Activator.CreateInstance(typeOfPage.GetViewType()) as IDetailPage;
|
||||
#else
|
||||
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
|
||||
|
@ -107,7 +107,7 @@ namespace TINK.View.Map
|
|||
return;
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
page.NavigationMasterDetail = NavigationMasterDetail;
|
||||
#endif
|
||||
|
||||
|
@ -120,7 +120,7 @@ namespace TINK.View.Map
|
|||
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
||||
#endif
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail { private get; set; }
|
||||
|
||||
|
@ -134,7 +134,7 @@ namespace TINK.View.Map
|
|||
// Pass reference to member Navigation to show bikes at station x dialog.
|
||||
try
|
||||
{
|
||||
Log.ForContext<MainPage>().Verbose("Constructing map page view model.");
|
||||
Log.ForContext<MapPage>().Verbose("Constructing map page view model.");
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
MapPageViewModel = new MapPageViewModel();
|
||||
|
@ -151,7 +151,7 @@ namespace TINK.View.Map
|
|||
} catch (Exception exception)
|
||||
{
|
||||
|
||||
Log.ForContext<MainPage>().Error("Constructing map page view model failed. {Exception}", exception);
|
||||
Log.ForContext<MapPage>().Error("Constructing map page view model failed. {Exception}", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -159,13 +159,13 @@ namespace TINK.View.Map
|
|||
{
|
||||
BindingContext = MapPageViewModel;
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
MapPageViewModel.NavigationMasterDetail = NavigationMasterDetail;
|
||||
#endif
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MainPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
|
||||
Log.ForContext<MapPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
// Continue because styling is not essential.
|
||||
Log.ForContext<MainPage>().Error("IOS specific styling of map page failed. {Exception}", exception);
|
||||
Log.ForContext<MapPage>().Error("IOS specific styling of map page failed. {Exception}", exception);
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -194,14 +194,14 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
// Continue because styling is not essential.
|
||||
Log.ForContext<MainPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Pre move and scanle maps to avoid initial display of map in Rome.
|
||||
Log.ForContext<MainPage>().Verbose("Moving and scaling map.");
|
||||
Log.ForContext<MapPage>().Verbose("Moving and scaling map.");
|
||||
MapPageViewModel.MoveAndScale(
|
||||
(mapSpan) => MyMap.MoveToRegion(mapSpan),
|
||||
App.ModelRoot.Uris.ActiveUri,
|
||||
|
@ -210,17 +210,17 @@ namespace TINK.View.Map
|
|||
catch(Exception exception)
|
||||
{
|
||||
// Continue because a map not beeing moved/ scaled is no reason for aborting startup.
|
||||
Log.ForContext<MainPage>().Error("Moving and scaling map failed. {Exception}", exception);
|
||||
Log.ForContext<MapPage>().Error("Moving and scaling map failed. {Exception}", exception);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Log.ForContext<MainPage>().Verbose("Invoking OnAppearing on map page view model.");
|
||||
Log.ForContext<MapPage>().Verbose("Invoking OnAppearing on map page view model.");
|
||||
await MapPageViewModel.OnAppearing();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MainPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace TINK.View.MiniSurvey
|
|||
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
||||
=> throw new NotImplementedException();
|
||||
#else
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using Xamarin.Forms;
|
||||
|
@ -138,7 +138,7 @@ namespace TINK.View.MyBikes
|
|||
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
||||
=> throw new NotImplementedException();
|
||||
#else
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using TINK.Model.Station;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using Xamarin.Forms;
|
||||
|
@ -18,7 +18,7 @@ namespace TINK.View.Root
|
|||
// - switch to login page form bikes at station page if not yet logged in
|
||||
/// </remarks>
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class RootPage : FlyoutPage, INavigationMasterDetail
|
||||
#else
|
||||
public partial class RootPage : FlyoutPage
|
||||
|
@ -38,7 +38,7 @@ namespace TINK.View.Root
|
|||
return;
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
var detailPage = navigationPage.RootPage as IDetailPage;
|
||||
if (detailPage == null)
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ namespace TINK.View.Root
|
|||
var page = (Page)Activator.CreateInstance(typeOfPage);
|
||||
page.Title = title;
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
if (page is IDetailPage detailPage)
|
||||
{
|
||||
// Detail page needs reference to perform navigation.
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using TINK.Model;
|
||||
using TINK.Services;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.View.Themes;
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="TINK.View.MainPage"
|
||||
xmlns:pages="clr-namespace:TINK.View"
|
||||
xmlns:mappage="clr-namespace:TINK.View.Map">
|
||||
<MasterDetailPage.Master>
|
||||
<pages:MasterPage x:Name="MasterPage" />
|
||||
</MasterDetailPage.Master>
|
||||
<MasterDetailPage.Detail>
|
||||
<NavigationPage>
|
||||
<x:Arguments>
|
||||
<mappage:MapPage />
|
||||
</x:Arguments>
|
||||
</NavigationPage>
|
||||
</MasterDetailPage.Detail>
|
||||
</MasterDetailPage>
|
|
@ -1,91 +0,0 @@
|
|||
using System;
|
||||
using TINK.View.Info.BikeInfo;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.ViewModel.MasterDetail;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace TINK.View
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate to perform navigation.
|
||||
/// </summary>
|
||||
public delegate void ShowPageDelegate(Type p_oType, string p_strTitle = null);
|
||||
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
public partial class MainPage : MasterDetailPage, INavigationMasterDetail
|
||||
#else
|
||||
public partial class MainPage : MasterDetailPage
|
||||
#endif
|
||||
{
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
MasterPage.ShowPageViewService = ShowPage;
|
||||
|
||||
// Any type of split behaviour conflics with map shifting functionality.
|
||||
MasterBehavior = MasterBehavior.Popover;
|
||||
|
||||
var navigationPage = Detail as NavigationPage;
|
||||
if (navigationPage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
var detailPage = navigationPage.RootPage as IDetailPage;
|
||||
if (detailPage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
detailPage.NavigationMasterDetail = this;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary> Creates and a page an shows it.</summary>
|
||||
/// <param name="p_oTypeOfPage">Type of page to show.</param>
|
||||
public void ShowPage(
|
||||
Type p_oTypeOfPage,
|
||||
string p_strTitle = null)
|
||||
{
|
||||
if (p_oTypeOfPage == null)
|
||||
return;
|
||||
|
||||
var page = (Page)Activator.CreateInstance(p_oTypeOfPage);
|
||||
page.Title = p_strTitle ?? Helper.GetCaption(p_oTypeOfPage);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
var l_oPage = page as IDetailPage;
|
||||
if (l_oPage != null)
|
||||
{
|
||||
l_oPage.NavigationMasterDetail = this;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !BACKSTYLE
|
||||
// When bike info page is shown do not allow to close this carousel before all pages were displayed.
|
||||
IsGestureEnabled = p_oTypeOfPage != typeof(BikeInfoCarouselPage);
|
||||
|
||||
Detail = new NavigationPage(page);
|
||||
IsPresented = false;
|
||||
#else
|
||||
Detail.Navigation.PushAsync(page);
|
||||
IsPresented = false;
|
||||
|
||||
while (Detail.Navigation.NavigationStack.Count > 2)
|
||||
{
|
||||
// Ensure that stack does never contains more than 2 pages (root page + one child)
|
||||
// This can occure if from child via swipe onther child is opened.
|
||||
// Remove item after adding new one to avoid flickering.
|
||||
Detail.Navigation.RemovePage(Detail.Navigation.NavigationStack[1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
MasterPage.ListView.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
|
||||
x:Class="TINK.View.MasterPage"
|
||||
xmlns:local="clr-namespace:TINK.View"
|
||||
Icon="Icon-Small@2x"
|
||||
Title="TINK">
|
||||
<StackLayout>
|
||||
<ListView x:Name="MenuItemsListView"
|
||||
SeparatorVisibility="None"
|
||||
HasUnevenRows="true"
|
||||
local:ListViewAttachedBehavior.Command="{Binding MenuItemSelected}"
|
||||
SelectedItem="{Binding SelectedMenuItem}"
|
||||
ItemsSource="{Binding MenuItems}">
|
||||
<ListView.Header>
|
||||
<Grid BackgroundColor="{DynamicResource Key=primary-back-title-color}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="10"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="10"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="30"/>
|
||||
<RowDefinition Height="80"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="10"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label
|
||||
Grid.Column="1"
|
||||
Grid.Row="2"
|
||||
FontSize="Large"
|
||||
Text="{Binding MasterDetailMenuTitlte}"
|
||||
Style="{DynamicResource SubtitleStyle}"/>
|
||||
</Grid>
|
||||
</ListView.Header>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<StackLayout Padding="15,10" HorizontalOptions="FillAndExpand">
|
||||
<Label VerticalOptions="FillAndExpand"
|
||||
VerticalTextAlignment="Center"
|
||||
Text="{Binding Title}"
|
||||
FontSize="24"/>
|
||||
</StackLayout>
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
</ContentPage>
|
|
@ -1,32 +0,0 @@
|
|||
using TINK.ViewModel;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace TINK.View
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class MasterPage : ContentPage
|
||||
{
|
||||
public ListView ListView;
|
||||
|
||||
/// <summary>
|
||||
/// Master page view model
|
||||
/// </summary>
|
||||
private MasterPageViewModel m_oMasterPageViewModel;
|
||||
|
||||
/// <summary> Delegate to perform navigation between detail pages. </summary>
|
||||
public ShowPageDelegate ShowPageViewService { set => m_oMasterPageViewModel.ShowPageViewService = value; }
|
||||
|
||||
public MasterPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
m_oMasterPageViewModel = new MasterPageViewModel();
|
||||
|
||||
|
||||
|
||||
BindingContext = m_oMasterPageViewModel;
|
||||
ListView = MenuItemsListView;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
using System.Threading.Tasks;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using System;
|
||||
|
@ -12,7 +12,7 @@ using Xamarin.CommunityToolkit.Extensions;
|
|||
namespace TINK.View.Settings
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public partial class SettingsPage : ContentPage, IViewService, IDetailPage
|
||||
#else
|
||||
public partial class SettingsPage : ContentPage, IViewService
|
||||
|
@ -88,7 +88,7 @@ namespace TINK.View.Settings
|
|||
public new async Task<string> DisplayActionSheet(String p_strTitle, String p_strCancel, String destruction, params String[] p_oButtons)
|
||||
=> await base.DisplayActionSheet(p_strTitle, p_strCancel, destruction, p_oButtons);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>
|
||||
/// Creates and a page an shows it.
|
||||
/// </summary>
|
||||
|
@ -110,7 +110,7 @@ namespace TINK.View.Settings
|
|||
public Task PopModalAsync()
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigation;
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace TINK.View.WhatsNew.Agb
|
|||
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
||||
=> throw new NotImplementedException();
|
||||
#else
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace TINK.View.WhatsNew
|
|||
await Navigation.PushModalAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
||||
=> throw new NotImplementedException();
|
||||
#else
|
||||
|
|
|
@ -16,7 +16,7 @@ using TINK.View.Root;
|
|||
using TINK.View.Settings;
|
||||
using Xamarin.Forms;
|
||||
using TINK.ViewModel.MasterDetail;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.Services;
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TINK.Services;
|
||||
using TINK.Services.CopriApi.ServerUris;
|
||||
using TINK.View;
|
||||
using TINK.View.Account;
|
||||
using TINK.View.Contact;
|
||||
using TINK.View.FindBike;
|
||||
using TINK.View.Info;
|
||||
using TINK.View.Login;
|
||||
using TINK.View.Map;
|
||||
using TINK.View.MyBikes;
|
||||
using TINK.View.Settings;
|
||||
using TINK.View.Themes;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// View model managing master detail menu.
|
||||
/// </summary>
|
||||
class MasterPageViewModel : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary to manage collection of menu items.
|
||||
/// </summary>
|
||||
private Dictionary<Type, int> m_oEntryDictionary;
|
||||
|
||||
/// <summary> Updates menu entries depending on state. </summary>
|
||||
private void UpdateMenuEntries()
|
||||
{
|
||||
var l_oSelectedMenuItem = SelectedMenuItem ?? new MainPageMenuItem(0, typeof(MapPage));
|
||||
|
||||
for (int l_iIndex = MenuItems.Count - 1; l_iIndex >= 0; l_iIndex--)
|
||||
{
|
||||
MenuItems.Clear();
|
||||
m_oEntryDictionary.Clear();
|
||||
}
|
||||
|
||||
CheckAddItem(typeof(MapPage));
|
||||
|
||||
if (App.ModelRoot.ActiveUser.IsLoggedIn)
|
||||
{
|
||||
CheckAddItem(typeof(MyBikesPage));
|
||||
CheckAddItem(typeof(FindBikePage));
|
||||
CheckAddItem(typeof(AccountPage));
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckAddItem(typeof(LoginPage));
|
||||
}
|
||||
|
||||
if (App.ModelRoot.Uris.ActiveUri.Host.GetIsCopri()
|
||||
|| App.ModelRoot.ActiveUser.IsLoggedIn)
|
||||
{
|
||||
CheckAddItem(typeof(SettingsPage));
|
||||
}
|
||||
|
||||
CheckAddItem(typeof(FeesAndBikesPage)); // Fees and bikes
|
||||
|
||||
CheckAddItem(typeof(ContactPage)); // Feedback and contact
|
||||
|
||||
CheckAddItem(typeof(TabbedPageInfo)); // About sharee.bike
|
||||
}
|
||||
|
||||
/// <summary>Adds a menu item to master detail menu.</summary>
|
||||
/// <param name="p_oType">Type decribing entry to be added.</param>
|
||||
private void CheckAddItem(Type p_oType)
|
||||
{
|
||||
if (m_oEntryDictionary.ContainsKey(p_oType))
|
||||
{
|
||||
// Nothing to do because has already been added.
|
||||
return;
|
||||
}
|
||||
|
||||
m_oEntryDictionary.Add(
|
||||
p_oType,
|
||||
m_oEntryDictionary.Count);
|
||||
|
||||
MenuItems.Add(
|
||||
new MainPageMenuItem(m_oEntryDictionary[p_oType],
|
||||
p_oType));
|
||||
}
|
||||
|
||||
/// <summary> Removes a page from menu.</summary>
|
||||
/// <param name="p_oType"></param>
|
||||
private void RemoveItem(Type p_oType)
|
||||
{
|
||||
if (!m_oEntryDictionary.ContainsKey(p_oType))
|
||||
{
|
||||
// Nothing to do because item was never added/ already removed.
|
||||
return;
|
||||
}
|
||||
|
||||
MenuItems.Remove(MenuItems[m_oEntryDictionary[p_oType]]);
|
||||
m_oEntryDictionary.Remove(p_oType);
|
||||
}
|
||||
|
||||
public MasterPageViewModel()
|
||||
{
|
||||
MenuItems = new ObservableCollection<MainPageMenuItem>();
|
||||
m_oEntryDictionary = new Dictionary<Type, int>();
|
||||
|
||||
App.ModelRoot.ActiveUser.StateChanged += (sender, eventargs) => OnUpdateRequired();
|
||||
|
||||
// Update flyout view model whenever theme is switched.
|
||||
App.ModelRoot.Themes.PropertyChanged += (sender, eventargs) =>
|
||||
{
|
||||
if (!(sender is ServicesContainerMutable<object> themes))
|
||||
return;
|
||||
|
||||
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(themes.Active);
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
|
||||
};
|
||||
|
||||
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(App.ModelRoot.Themes.Active);
|
||||
|
||||
UpdateMenuEntries();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flyout title from theme name
|
||||
/// </summary>
|
||||
/// <param name="theme">Name of theme.</param>
|
||||
/// <returns>Flyout title.</returns>
|
||||
private string GetMasterDetailMenuTitle(object theme)
|
||||
{
|
||||
if (!(theme is ITheme active))
|
||||
return "sharee.bike";
|
||||
|
||||
return $"sharee.bike{(!string.IsNullOrEmpty(active.OperatorInfo) ? ($"\r\n{active.OperatorInfo}") : string.Empty)}";
|
||||
}
|
||||
|
||||
private string masterDetailMenuTitlte;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the title of the fylout page.
|
||||
/// </summary>
|
||||
public string MasterDetailMenuTitlte
|
||||
{
|
||||
get
|
||||
{
|
||||
return masterDetailMenuTitlte;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (masterDetailMenuTitlte == value)
|
||||
return;
|
||||
|
||||
masterDetailMenuTitlte = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides collection of menu items for master page.
|
||||
/// </summary>
|
||||
public ObservableCollection<MainPageMenuItem> MenuItems
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View service used to manage menu state.
|
||||
/// </summary>
|
||||
public ShowPageDelegate ShowPageViewService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item.
|
||||
/// </summary>
|
||||
public MainPageMenuItem SelectedMenuItem
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command object which is invoked from view when an item is selected.
|
||||
/// </summary>
|
||||
public Command MenuItemSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Command(OnUpdateRequired);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary> Manges updates required due to selection of a menu or login events.</summary>
|
||||
public void OnUpdateRequired()
|
||||
{
|
||||
if (ShowPageViewService == null)
|
||||
return;
|
||||
|
||||
UpdateMenuEntries();
|
||||
|
||||
if (SelectedMenuItem == null)
|
||||
{
|
||||
// Nothing to do if no menu entry is selected.
|
||||
return;
|
||||
}
|
||||
|
||||
ShowPageViewService(SelectedMenuItem.TargetType);
|
||||
}
|
||||
|
||||
#region INotifyPropertyChanged Implementation
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
void OnPropertyChanged([CallerMemberName] string propertyName = "")
|
||||
{
|
||||
if (PropertyChanged == null)
|
||||
return;
|
||||
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -425,6 +425,10 @@ namespace TINK.Model
|
|||
{
|
||||
new Version(3, 0, 243),
|
||||
AppResources.ChangeLog3_0_243
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 244),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
}
|
||||
};
|
||||
|
||||
|
|
153
TINKLib/MultilingualResources/AppResources.Designer.cs
generated
153
TINKLib/MultilingualResources/AppResources.Designer.cs
generated
|
@ -312,6 +312,24 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Can not query location info..
|
||||
/// </summary>
|
||||
public static string ActivityTextErrorQueryLocationQuery {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTextErrorQueryLocationQuery", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No location info available..
|
||||
/// </summary>
|
||||
public static string ActivityTextErrorQueryLocationWhenAny {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTextErrorQueryLocationWhenAny", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Battery status cannot be read..
|
||||
/// </summary>
|
||||
|
@ -438,6 +456,33 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Query location....
|
||||
/// </summary>
|
||||
public static string ActivityTextQueryLocation {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTextQueryLocation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel query location....
|
||||
/// </summary>
|
||||
public static string ActivityTextQueryLocationCancelWait {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTextQueryLocationCancelWait", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Start query location....
|
||||
/// </summary>
|
||||
public static string ActivityTextQueryLocationStart {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTextQueryLocationStart", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reading charging level....
|
||||
/// </summary>
|
||||
|
@ -456,6 +501,15 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Asking for permissions....
|
||||
/// </summary>
|
||||
public static string ActivityTextRequestingLocationPermissions {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTextRequestingLocationPermissions", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reserving bike....
|
||||
/// </summary>
|
||||
|
@ -465,6 +519,15 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Returning bike....
|
||||
/// </summary>
|
||||
public static string ActivityTextReturningBike {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTextReturningBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Searching locks....
|
||||
/// </summary>
|
||||
|
@ -779,6 +842,15 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Closing lock and returning bike speeded up..
|
||||
/// </summary>
|
||||
public static string ChangeLog3_0_244 {
|
||||
get {
|
||||
return ResourceManager.GetString("ChangeLog3_0_244", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Lock of rented bike can not be found..
|
||||
/// </summary>
|
||||
|
@ -965,6 +1037,24 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Internet must be available when returning the bike..
|
||||
/// </summary>
|
||||
public static string ErrorReturnBikeNoWebMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorReturnBikeNoWebMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection error when returning the bike!.
|
||||
/// </summary>
|
||||
public static string ErrorReturnBikeNoWebTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorReturnBikeNoWebTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your feedback could not be send to server successfully..
|
||||
/// </summary>
|
||||
|
@ -1514,6 +1604,33 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Closing the lock and ending the rental is not possible..
|
||||
/// </summary>
|
||||
public static string MessageErrorQueryLocationMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("MessageErrorQueryLocationMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error Start Query Location!.
|
||||
/// </summary>
|
||||
public static string MessageErrorQueryLocationStartTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("MessageErrorQueryLocationStartTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error Query Location!.
|
||||
/// </summary>
|
||||
public static string MessageErrorQueryLocationTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("MessageErrorQueryLocationTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Login cookie must not be empty. {0}.
|
||||
/// </summary>
|
||||
|
@ -1607,15 +1724,6 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Rent bike {0} and open lock?.
|
||||
/// </summary>
|
||||
public static string MessageOpenLockAndBookeBike {
|
||||
get {
|
||||
return ResourceManager.GetString("MessageOpenLockAndBookeBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Urgent questions?.
|
||||
/// </summary>
|
||||
|
@ -1733,6 +1841,24 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Close lock and return bike {0}?.
|
||||
/// </summary>
|
||||
public static string QuestionCloseLockAndReturnBike {
|
||||
get {
|
||||
return ResourceManager.GetString("QuestionCloseLockAndReturnBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Rent bike {0} and open lock?.
|
||||
/// </summary>
|
||||
public static string QuestionOpenLockAndBookBike {
|
||||
get {
|
||||
return ResourceManager.GetString("QuestionOpenLockAndBookBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reserve bike {0} free of charge for {1} min?.
|
||||
/// </summary>
|
||||
|
@ -1742,6 +1868,15 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Return bike {0}?.
|
||||
/// </summary>
|
||||
public static string QuestionReturnBike {
|
||||
get {
|
||||
return ResourceManager.GetString("QuestionReturnBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} app request.
|
||||
/// </summary>
|
||||
|
|
|
@ -374,9 +374,6 @@ Freigabedialog öffen?</value>
|
|||
Software Pakete aktualisiert.
|
||||
Zielplatform Android 11.</value>
|
||||
</data>
|
||||
<data name="MessageOpenLockAndBookeBike" xml:space="preserve">
|
||||
<value>Fahrrad {0} mieten und Schloss öffnen?</value>
|
||||
</data>
|
||||
<data name="ActivityTextReservingBike" xml:space="preserve">
|
||||
<value>Reserviere Rad...</value>
|
||||
</data>
|
||||
|
@ -653,4 +650,53 @@ Layout Anzeige Radnamen und nummern verbessert.</value>
|
|||
<value>Miniumfrage implementiert.
|
||||
Kleinere Verbesserungen.</value>
|
||||
</data>
|
||||
<data name="ActivityTextRequestingLocationPermissions" xml:space="preserve">
|
||||
<value>Anfrage nach Berechtigungen...</value>
|
||||
</data>
|
||||
<data name="ActivityTextQueryLocation" xml:space="preserve">
|
||||
<value>Abfrage Standort...</value>
|
||||
</data>
|
||||
<data name="MessageErrorQueryLocationTitle" xml:space="preserve">
|
||||
<value>Fehler bei Standortabfrage!</value>
|
||||
</data>
|
||||
<data name="MessageErrorQueryLocationMessage" xml:space="preserve">
|
||||
<value>Schloss schließen und Miete beenden ist nicht möglich.</value>
|
||||
</data>
|
||||
<data name="MessageErrorQueryLocationStartTitle" xml:space="preserve">
|
||||
<value>Fehler beim Start der Standortabfrage!</value>
|
||||
</data>
|
||||
<data name="ActivityTextErrorQueryLocationWhenAny" xml:space="preserve">
|
||||
<value>Keine Standortinfo verfügbar.</value>
|
||||
</data>
|
||||
<data name="ActivityTextErrorQueryLocationQuery" xml:space="preserve">
|
||||
<value>Standortabfrage nicht möglich.</value>
|
||||
</data>
|
||||
<data name="ActivityTextQueryLocationCancelWait" xml:space="preserve">
|
||||
<value>Abbruch Standortabfrage...</value>
|
||||
</data>
|
||||
<data name="ActivityTextQueryLocationStart" xml:space="preserve">
|
||||
<value>Start Standortabfrage...</value>
|
||||
</data>
|
||||
<data name="QuestionCloseLockAndReturnBike" xml:space="preserve">
|
||||
<value>Fahrrad {0} abschließen und zurückgeben?</value>
|
||||
</data>
|
||||
<data name="QuestionReturnBike" xml:space="preserve">
|
||||
<value>Fahrrad {0} zurückgeben?
|
||||
</value>
|
||||
</data>
|
||||
<data name="ActivityTextReturningBike" xml:space="preserve">
|
||||
<value>Gebe Rad zurück...</value>
|
||||
</data>
|
||||
<data name="QuestionOpenLockAndBookBike" xml:space="preserve">
|
||||
<value>Fahrrad {0} mieten und Schloss öffnen?</value>
|
||||
</data>
|
||||
<data name="ErrorReturnBikeNoWebMessage" xml:space="preserve">
|
||||
<value>Internet muss erreichbar sein beim Zurückgeben des Rads.</value>
|
||||
</data>
|
||||
<data name="ErrorReturnBikeNoWebTitle" xml:space="preserve">
|
||||
<value>Verbingungsfehler beim Zurückgeben des Rads!</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_244" xml:space="preserve">
|
||||
<value>Abschließen von Rad und Radrückgabe beschleunigt.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -482,7 +482,7 @@ Targets Android 11.</value>
|
|||
<data name="ActivityTextReservingBike" xml:space="preserve">
|
||||
<value>Reserving bike...</value>
|
||||
</data>
|
||||
<data name="MessageOpenLockAndBookeBike" xml:space="preserve">
|
||||
<data name="QuestionOpenLockAndBookBike" xml:space="preserve">
|
||||
<value>Rent bike {0} and open lock?</value>
|
||||
</data>
|
||||
<data name="ActivityTextErrorReadingChargingLevelGeneral" xml:space="preserve">
|
||||
|
@ -749,4 +749,49 @@ Layout of bike names and id display improved.</value>
|
|||
<value>Mini survey implemented.
|
||||
Minor fixes.</value>
|
||||
</data>
|
||||
<data name="ActivityTextRequestingLocationPermissions" xml:space="preserve">
|
||||
<value>Asking for permissions...</value>
|
||||
</data>
|
||||
<data name="ActivityTextErrorQueryLocationQuery" xml:space="preserve">
|
||||
<value>Can not query location info.</value>
|
||||
</data>
|
||||
<data name="ActivityTextErrorQueryLocationWhenAny" xml:space="preserve">
|
||||
<value>No location info available.</value>
|
||||
</data>
|
||||
<data name="ActivityTextQueryLocation" xml:space="preserve">
|
||||
<value>Query location...</value>
|
||||
</data>
|
||||
<data name="ActivityTextQueryLocationCancelWait" xml:space="preserve">
|
||||
<value>Cancel query location...</value>
|
||||
</data>
|
||||
<data name="ActivityTextQueryLocationStart" xml:space="preserve">
|
||||
<value>Start query location...</value>
|
||||
</data>
|
||||
<data name="MessageErrorQueryLocationMessage" xml:space="preserve">
|
||||
<value>Closing the lock and ending the rental is not possible.</value>
|
||||
</data>
|
||||
<data name="MessageErrorQueryLocationStartTitle" xml:space="preserve">
|
||||
<value>Error Start Query Location!</value>
|
||||
</data>
|
||||
<data name="MessageErrorQueryLocationTitle" xml:space="preserve">
|
||||
<value>Error Query Location!</value>
|
||||
</data>
|
||||
<data name="QuestionCloseLockAndReturnBike" xml:space="preserve">
|
||||
<value>Close lock and return bike {0}?</value>
|
||||
</data>
|
||||
<data name="ActivityTextReturningBike" xml:space="preserve">
|
||||
<value>Returning bike...</value>
|
||||
</data>
|
||||
<data name="QuestionReturnBike" xml:space="preserve">
|
||||
<value>Return bike {0}?</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_244" xml:space="preserve">
|
||||
<value>Closing lock and returning bike speeded up.</value>
|
||||
</data>
|
||||
<data name="ErrorReturnBikeNoWebMessage" xml:space="preserve">
|
||||
<value>Internet must be available when returning the bike.</value>
|
||||
</data>
|
||||
<data name="ErrorReturnBikeNoWebTitle" xml:space="preserve">
|
||||
<value>Connection error when returning the bike!</value>
|
||||
</data>
|
||||
</root>
|
|
@ -496,10 +496,6 @@ Targets Android 11.</source>
|
|||
Software Pakete aktualisiert.
|
||||
Zielplatform Android 11.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MessageOpenLockAndBookeBike" translate="yes" xml:space="preserve">
|
||||
<source>Rent bike {0} and open lock?</source>
|
||||
<target state="translated">Fahrrad {0} mieten und Schloss öffnen?</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextReservingBike" translate="yes" xml:space="preserve">
|
||||
<source>Reserving bike...</source>
|
||||
<target state="translated">Reserviere Rad...</target>
|
||||
|
@ -875,6 +871,71 @@ Minor fixes.</source>
|
|||
<target state="translated">Miniumfrage implementiert.
|
||||
Kleinere Verbesserungen.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextRequestingLocationPermissions" translate="yes" xml:space="preserve">
|
||||
<source>Asking for permissions...</source>
|
||||
<target state="translated">Anfrage nach Berechtigungen...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextQueryLocation" translate="yes" xml:space="preserve">
|
||||
<source>Query location...</source>
|
||||
<target state="translated">Abfrage Standort...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MessageErrorQueryLocationTitle" translate="yes" xml:space="preserve">
|
||||
<source>Error Query Location!</source>
|
||||
<target state="translated">Fehler bei Standortabfrage!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MessageErrorQueryLocationMessage" translate="yes" xml:space="preserve">
|
||||
<source>Closing the lock and ending the rental is not possible.</source>
|
||||
<target state="translated">Schloss schließen und Miete beenden ist nicht möglich.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MessageErrorQueryLocationStartTitle" translate="yes" xml:space="preserve">
|
||||
<source>Error Start Query Location!</source>
|
||||
<target state="translated">Fehler beim Start der Standortabfrage!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextErrorQueryLocationWhenAny" translate="yes" xml:space="preserve">
|
||||
<source>No location info available.</source>
|
||||
<target state="translated">Keine Standortinfo verfügbar.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextErrorQueryLocationQuery" translate="yes" xml:space="preserve">
|
||||
<source>Can not query location info.</source>
|
||||
<target state="translated">Standortabfrage nicht möglich.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextQueryLocationCancelWait" translate="yes" xml:space="preserve">
|
||||
<source>Cancel query location...</source>
|
||||
<target state="translated">Abbruch Standortabfrage...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextQueryLocationStart" translate="yes" xml:space="preserve">
|
||||
<source>Start query location...</source>
|
||||
<target state="translated">Start Standortabfrage...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="QuestionCloseLockAndReturnBike" translate="yes" xml:space="preserve">
|
||||
<source>Close lock and return bike {0}?</source>
|
||||
<target state="translated">Fahrrad {0} abschließen und zurückgeben?</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="QuestionReturnBike" translate="yes" xml:space="preserve">
|
||||
<source>Return bike {0}?</source>
|
||||
<target state="translated">Fahrrad {0} zurückgeben?
|
||||
</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextReturningBike" translate="yes" xml:space="preserve">
|
||||
<source>Returning bike...</source>
|
||||
<target state="translated">Gebe Rad zurück...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="QuestionOpenLockAndBookBike" translate="yes" xml:space="preserve">
|
||||
<source>Rent bike {0} and open lock?</source>
|
||||
<target state="translated">Fahrrad {0} mieten und Schloss öffnen?</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ErrorReturnBikeNoWebMessage" translate="yes" xml:space="preserve">
|
||||
<source>Internet must be available when returning the bike.</source>
|
||||
<target state="translated">Internet muss erreichbar sein beim Zurückgeben des Rads.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ErrorReturnBikeNoWebTitle" translate="yes" xml:space="preserve">
|
||||
<source>Connection error when returning the bike!</source>
|
||||
<target state="translated">Verbingungsfehler beim Zurückgeben des Rads!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ChangeLog3_0_244" translate="yes" xml:space="preserve">
|
||||
<source>Closing lock and returning bike speeded up.</source>
|
||||
<target state="translated">Abschließen von Rad und Radrückgabe beschleunigt.</target>
|
||||
</trans-unit>
|
||||
</group>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
using Xamarin.Essentials;
|
||||
|
@ -22,13 +23,17 @@ namespace TINK.Model.Services.Geolocation
|
|||
|
||||
public bool IsGeolcationEnabled => Dependent.IsGeolcationEnabled;
|
||||
|
||||
/// <summary> Gets the current location.</summary>
|
||||
/// <param name="cancellationToken">Token to cancel request for geolocation.</param>
|
||||
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine whether cached geoloation can be used or not.</param>
|
||||
public async Task<Location> GetAsync(DateTime? timeStamp = null)
|
||||
public async Task<Location> GetAsync(CancellationToken? cancellationToken = null, DateTime? timeStamp = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromMilliseconds(GEOLOCATIONREQUEST_TIMEOUT_MS));
|
||||
return await Xamarin.Essentials.Geolocation.GetLocationAsync(request);
|
||||
return cancellationToken.HasValue
|
||||
? await Xamarin.Essentials.Geolocation.GetLocationAsync(request, cancellationToken.Value)
|
||||
: await Xamarin.Essentials.Geolocation.GetLocationAsync(request);
|
||||
}
|
||||
catch (FeatureNotSupportedException fnsEx)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
using Xamarin.Essentials;
|
||||
|
@ -9,9 +10,10 @@ namespace TINK.Model.Services.Geolocation
|
|||
public interface IGeolocation : IGeolodationDependent
|
||||
{
|
||||
/// <summary> Gets the current location.</summary>
|
||||
/// <param name="cancellationToken">Token to cancel request for geolocation. If null request can not be cancels and times out after GeolocationService.GEOLOCATIONREQUEST_TIMEOUT_MS if geolocation is not available.</param>
|
||||
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine for some implementations whether cached geoloation can be used or not.</param>
|
||||
/// <returns></returns>
|
||||
Task<Location> GetAsync(DateTime? timeStamp = null);
|
||||
Task<Location> GetAsync(CancellationToken? cancellationToken = null, DateTime? timeStamp = null);
|
||||
|
||||
/// <summary> If true location data returned is simulated.</summary>
|
||||
bool IsSimulation { get; }
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
using Xamarin.Essentials;
|
||||
|
@ -8,24 +9,22 @@ namespace TINK.Model.Services.Geolocation
|
|||
{
|
||||
public class LastKnownGeolocationService : IGeolocation
|
||||
{
|
||||
/// <summary> Timeout for geolocation request operations.</summary>
|
||||
private const int GEOLOCATIONREQUEST_TIMEOUT_MS = 5000;
|
||||
|
||||
private IGeolodationDependent Dependent { get; }
|
||||
|
||||
public LastKnownGeolocationService(IGeolodationDependent dependent)
|
||||
{
|
||||
Dependent = dependent;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Gets the current location.</summary>
|
||||
/// <param name="cancelationToken">Token to cancel request for geolocation.</param>
|
||||
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine whether cached geoloation can be used or not.</param>
|
||||
public async Task<Location> GetAsync(DateTime? timeStamp = null)
|
||||
public async Task<Location> GetAsync(CancellationToken? cancelationToken = null, DateTime? timeStamp = null)
|
||||
{
|
||||
Location location;
|
||||
try
|
||||
{
|
||||
var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromMilliseconds(GEOLOCATIONREQUEST_TIMEOUT_MS));
|
||||
location = await Xamarin.Essentials.Geolocation.GetLocationAsync(request);
|
||||
location = await Xamarin.Essentials.Geolocation.GetLastKnownLocationAsync();
|
||||
}
|
||||
catch (FeatureNotSupportedException fnsEx)
|
||||
{
|
||||
|
@ -59,13 +58,14 @@ namespace TINK.Model.Services.Geolocation
|
|||
return location;
|
||||
}
|
||||
|
||||
return await Xamarin.Essentials.Geolocation.GetLocationAsync();
|
||||
return await new GeolocationService(Dependent).GetAsync(cancelationToken, timeStamp);
|
||||
}
|
||||
|
||||
/// <summary> If true location data returned is simulated.</summary>
|
||||
public bool IsSimulation { get => false; }
|
||||
|
||||
public TimeSpan MaxAge => new TimeSpan(0, 3, 0);
|
||||
/// <summary> Maximum age allowed for location info. </summary>
|
||||
public TimeSpan MaxAge => new TimeSpan(0, 3 /*minutes*/, 0);
|
||||
|
||||
public bool IsGeolcationEnabled => Dependent.IsGeolcationEnabled;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
using Xamarin.Essentials;
|
||||
|
@ -14,7 +15,10 @@ namespace TINK.Model.Services.Geolocation
|
|||
Dependent = dependent;
|
||||
}
|
||||
|
||||
public async Task<Location> GetAsync(DateTime? timeStamp = null)
|
||||
/// <summary> Gets the current location.</summary>
|
||||
/// <param name="cancelToken">Token to cancel request for geolocation.</param>
|
||||
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine whether cached geoloation can be used or not.</param>
|
||||
public async Task<Location> GetAsync(CancellationToken? cancelToken = null, DateTime? timeStamp = null)
|
||||
{
|
||||
return await Task.FromResult(new Location(47.976634, 7.825490) { Accuracy = 0, Timestamp = timeStamp ?? DateTime.Now }); ;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace TINK.View
|
|||
/// <returns>T</returns>
|
||||
Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons);
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary> Shows a page.</summary>
|
||||
/// <param name="type">Type of page to show.</param>
|
||||
/// <param name="title">Title of page to show.</param>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using Serilog;
|
||||
using System;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
|
||||
namespace TINK.View
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using System;
|
||||
|
||||
namespace TINK.View.MasterDetail
|
||||
|
|
|
@ -325,7 +325,7 @@ namespace TINK.ViewModel.Account
|
|||
try
|
||||
{
|
||||
// Switch to map view after log out.
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
m_oViewService.ShowPage(ViewTypes.MapPage);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
try
|
||||
{
|
||||
// Switch to login page
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
|
|
|
@ -16,6 +16,8 @@ using Xamarin.Essentials;
|
|||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -64,18 +66,54 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> ReturnBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.IsIdle = false;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
|
||||
// Check if bike is around.
|
||||
var deviceState = LockService[SelectedBike.LockInfo.Id].GetDeviceState();
|
||||
if (deviceState == DeviceState.Connected)
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart; // Bluetooth is in reach
|
||||
|
||||
// Start getting geolocation.
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedClosed>().Information("Getting geolocation when returning bike {Bike} failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
$"Fahrrad {SelectedBike.GetFullDisplayName()} zurückgeben?",
|
||||
"Ja",
|
||||
"Nein");
|
||||
string.Format(AppResources.QuestionReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User aborted returning bike process
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {l_oId} in order to return but action was canceled.", SelectedBike.Id);
|
||||
|
||||
// Cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedClosed>().Information("Canceling query location failed on abort returning closed bike failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
@ -88,23 +126,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Check if bike is around.
|
||||
Location currentLocation = null;
|
||||
LocationDto currentLocationDto = null;
|
||||
var deviceState = LockService[SelectedBike.LockInfo.Id].GetDeviceState();
|
||||
if (deviceState == DeviceState.Connected)
|
||||
{
|
||||
// Bluetooth is in reach
|
||||
// Get geoposition to pass when returning.
|
||||
var timeStamp = DateTime.Now;
|
||||
BikesViewModel.ActionText = "Abfrage Standort...";
|
||||
Location currentLocation = null;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
try
|
||||
{
|
||||
currentLocation = await Geolocation.GetAsync(timeStamp);
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedClosed>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
|
||||
Log.ForContext<BookedClosed>().Information("Returning closed bike {Bike} is not possible. Cancel geolocation query failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
currentLocationDto = currentLocation != null
|
||||
|
@ -123,7 +158,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
SelectedBike.LockInfo.State = LockingState.Disconnected;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
BikesViewModel.ActionText = "Returning bike...";
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
|
@ -146,10 +181,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Zurückgeben des Rads!",
|
||||
string.Format("{0}\r\n{1}\r\n{2}", "Internet muss erreichbar sein zum Zurückgeben des Rads.", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
|
@ -159,7 +195,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeNotAtStationTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
|
@ -169,7 +205,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeNotAtStationTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockClosedNoGPSMessage),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
|
@ -188,7 +224,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Zurückgeben des Rads!",
|
||||
exception.Message, "OK");
|
||||
exception.Message,
|
||||
"OK");
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
|
@ -208,7 +245,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
@ -230,11 +267,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
Log.ForContext<BookedClosed>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<BookedClosed>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
|
@ -314,7 +351,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected booked bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<BookedDisconnected>().Information("User selected booked bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler bei Verbinden mit Schloss!",
|
||||
|
@ -102,7 +102,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected booked bike {l_oId} to connect to lock. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<BookedDisconnected>().Error("User selected booked bike {l_oId} to connect to lock. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler bei Verbinden mit Schloss!",
|
||||
|
|
|
@ -16,6 +16,8 @@ using TINK.Model.User;
|
|||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -62,18 +64,62 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> CloseLockAndReturnBike()
|
||||
{
|
||||
// Ask whether to really return bike?
|
||||
// Prevent concurrent interaction
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorQueryLocationStartTitle,
|
||||
$"{AppResources.MessageErrorQueryLocationMessage}\r\n{ex.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
$"Fahrrad {SelectedBike.GetFullDisplayName()} abschließen und zurückgeben?",
|
||||
"Ja",
|
||||
"Nein");
|
||||
string.Format(AppResources.QuestionCloseLockAndReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User aborted closing and returning bike process
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {l_oId} in order to close and return but action was canceled.", SelectedBike.Id);
|
||||
|
||||
// Cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on abort returning opened bike failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
@ -85,6 +131,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
|
@ -94,6 +141,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
@ -101,7 +151,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
|
@ -110,7 +160,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
|
@ -119,7 +169,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -128,13 +178,25 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.Disconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
|
@ -146,14 +208,29 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (SelectedBike.LockInfo.State != LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error($"Lock can not be closed. Invalid locking state state {SelectedBike.LockInfo.State} detected.");
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
SelectedBike.LockInfo.State == LockingState.Open
|
||||
? AppResources.ErrorCloseLockStillOpenMessage
|
||||
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
|
||||
"OK");
|
||||
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on unexpected lock state failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
@ -163,24 +240,24 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
var timeStamp = DateTime.Now;
|
||||
BikesViewModel.ActionText = "Abfrage Standort...";
|
||||
Location currentLocation;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
currentLocation = await Geolocation.GetAsync(timeStamp);
|
||||
var task = await Task.WhenAny(new List<Task> { currentLocationTask });
|
||||
currentLocation = currentLocationTask.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler bei Standortabfrage!",
|
||||
string.Format($"Schloss schließen und Miete beenden ist nicht möglich.\r\n{ex.Message}"),
|
||||
"OK");
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageErrorQueryLocationTitle,
|
||||
AppResources.MessageErrorQueryLocationMessage,
|
||||
ex.GetErrorMessage(),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
@ -191,7 +268,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
|
@ -222,10 +299,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Zurückgeben des Rads!",
|
||||
string.Format("{0}\r\n{1}\r\n{2}", "Internet muss erreichbar sein beim Zurückgeben des Rads.", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
|
@ -235,7 +313,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeNotAtStationTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
|
@ -245,7 +323,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeNotAtStationTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockOpenNoGPSMessage),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
|
@ -256,7 +334,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -283,7 +361,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
@ -345,12 +423,29 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedOpen>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Closing lock of bike {Bike} is not possible. Starting query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.Disconnected;
|
||||
|
@ -359,13 +454,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
|
@ -374,7 +472,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
|
@ -383,7 +481,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -391,34 +489,47 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.Disconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
var timeStamp = DateTime.Now;
|
||||
BikesViewModel.ActionText = "Abfrage Standort...";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
currentLocation = await Geolocation.GetAsync(timeStamp);
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask });
|
||||
currentLocation = currentLocationTask.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
|
||||
Log.ForContext<BookedOpen>().Information("Getting geolocation when closing lock of bike {Bike} failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = "Keine Standortinformationen verfügbar.";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
|
|
|
@ -15,6 +15,8 @@ using TINK.Model.User;
|
|||
using Xamarin.Essentials;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -100,7 +102,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
|
@ -206,14 +208,32 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.Disconnected;
|
||||
|
@ -222,6 +242,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
|
@ -261,27 +284,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.Disconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Canceling query location failed on unexpected lock state failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
var timeStamp = DateTime.Now;
|
||||
BikesViewModel.ActionText = "Abfrage Standort...";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
currentLocation = await Geolocation.GetAsync(timeStamp);
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
|
||||
Log.ForContext<BookedUnknown>().Information("Get geolocation failed when closing lock of bike {Bike} with unknown state. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = "Keine Standortinformationen verfügbar.";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
|
|
|
@ -187,7 +187,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Ask whether to really book bike?
|
||||
alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()),
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
|
@ -287,7 +287,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
|
|
|
@ -94,8 +94,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Close lock
|
||||
Log.ForContext<DisposableOpen>().Information("User selected disposable bike {bike} in order to close lock.", SelectedBike);
|
||||
|
||||
// Unlock bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
|
@ -115,7 +113,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
|
@ -124,7 +122,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
|
@ -160,7 +158,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
@ -249,7 +247,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
try
|
||||
{
|
||||
// Switch to map page
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
|
|
|
@ -167,7 +167,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Ask whether to really book bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()),
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
|
@ -244,7 +244,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -253,7 +253,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
|
@ -263,7 +263,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -337,20 +337,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -270,7 +270,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Ask whether to really book bike?
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()),
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
|
@ -370,7 +370,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
|
|
|
@ -171,7 +171,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Close lock and cancel reservation.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
|
@ -182,7 +181,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
|
@ -191,7 +190,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock bike is moving. {Exception}", exception);
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock bike is moving. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
|
@ -200,7 +199,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
|
@ -209,7 +208,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
Log.ForContext<ReservedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
|
|
|
@ -15,6 +15,8 @@ using TINK.Model.User;
|
|||
using Xamarin.Essentials;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -54,7 +56,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1()
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await OpenLock();
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
|
@ -93,7 +98,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
|
@ -195,18 +200,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.Disconnected;
|
||||
|
@ -215,6 +242,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
|
@ -254,27 +284,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.Disconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
var timeStamp = DateTime.Now;
|
||||
BikesViewModel.ActionText = "Abfrage Standort...";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
currentLocation = await Geolocation.GetAsync(timeStamp);
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = "Keine Standortinformationen verfügbar.";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
|
|
|
@ -145,7 +145,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
/// <summary> Command object to bind login page redirect link to view model.</summary>
|
||||
public System.Windows.Input.ICommand ContactSupportClickedCommand
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenSupportPageAsync());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
|
||||
|
@ -153,14 +153,14 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
/// <summary> Command object to bind login page redirect link to view model.</summary>
|
||||
public System.Windows.Input.ICommand LoginRequiredHintClickedCommand
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenLoginPageAsync());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Opens login page. </summary>
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void OpenLoginPageAsync()
|
||||
#else
|
||||
public async Task OpenLoginPageAsync()
|
||||
|
@ -170,7 +170,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
{
|
||||
// Switch to map page
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
|
@ -184,7 +184,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
}
|
||||
|
||||
/// <summary> Opens support. </summary>
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void OpenSupportPageAsync()
|
||||
#else
|
||||
public async Task OpenSupportPageAsync()
|
||||
|
@ -194,7 +194,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
{
|
||||
// Switch to map page
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingFeedbackAndContact);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
|
|
|
@ -166,14 +166,14 @@ namespace TINK.ViewModel.Info
|
|||
|
||||
/// <summary> Command object to bind login button to view model. </summary>
|
||||
public ICommand OnSelectStationRequest
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenSelectStationPage());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenSelectStationPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Opens login page. </summary>
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
public void OpenSelectStationPage()
|
||||
#else
|
||||
public async Task OpenSelectStationPageAsync()
|
||||
|
@ -183,7 +183,7 @@ namespace TINK.ViewModel.Info
|
|||
{
|
||||
// Switch to map page
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
ViewService.PushAsync(ViewTypes.SelectStationPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//SelectStationPage");
|
||||
|
|
|
@ -12,7 +12,7 @@ using System.Threading.Tasks;
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms.GoogleMaps;
|
||||
using System.Collections.ObjectModel;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.Settings;
|
||||
|
@ -66,7 +66,7 @@ namespace TINK.ViewModel.Contact
|
|||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigation m_oNavigation;
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigationMasterDetail;
|
||||
#endif
|
||||
|
@ -142,14 +142,14 @@ namespace TINK.ViewModel.Contact
|
|||
m_oNavigation = navigation
|
||||
?? throw new ArgumentException("Can not instantiate map page view model- object. No navigation service available.");
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
|
||||
#endif
|
||||
|
||||
IsConnected = TinkApp.GetIsConnected();
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail
|
||||
{
|
||||
|
@ -285,7 +285,7 @@ namespace TINK.ViewModel.Contact
|
|||
|
||||
if (Pins.Count <= 0)
|
||||
{
|
||||
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
|
||||
ActionText = AppResources.ActivityTextRequestingLocationPermissions;
|
||||
|
||||
// Check location permission
|
||||
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace TINK.ViewModel.FindBike
|
|||
{
|
||||
Log.ForContext<FindBikePageViewModel>().Information("User request to show page FindBike- page re-appearing");
|
||||
|
||||
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
|
||||
ActionText = AppResources.ActivityTextFindBikeLoadingBikes;
|
||||
|
||||
var bikes = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ namespace TINK.ViewModel.Info.BikeInfo
|
|||
public IList<CourouselPageItemViewModel> CarouselItems { get; }
|
||||
|
||||
/// <summary> Command object to bind close button to view model. </summary>
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
private Action CloseAction
|
||||
=> () => m_oViewService.ShowPage(ViewTypes.MapPage);
|
||||
#else
|
||||
|
|
|
@ -317,7 +317,7 @@ namespace TINK.ViewModel
|
|||
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.FILTERTINKGENERAL))
|
||||
{
|
||||
// No need to show "Anleitung TINK Räder" because user can not use tink.
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
m_oViewService.ShowPage(ViewTypes.MapPage);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
|
@ -326,7 +326,7 @@ namespace TINK.ViewModel
|
|||
}
|
||||
|
||||
// Swich to map page
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
|
|
|
@ -12,7 +12,7 @@ using System.Threading.Tasks;
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms.GoogleMaps;
|
||||
using System.Collections.ObjectModel;
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.Settings;
|
||||
|
@ -74,7 +74,7 @@ namespace TINK.ViewModel.Map
|
|||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigation m_oNavigation;
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigationMasterDetail;
|
||||
#endif
|
||||
|
@ -151,7 +151,7 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
m_oViewUpdateManager = new IdlePollingUpdateTaskManager();
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
|
||||
#endif
|
||||
|
||||
|
@ -176,7 +176,7 @@ namespace TINK.ViewModel.Map
|
|||
}
|
||||
}
|
||||
|
||||
#if USEMASTERDETAIL || USEFLYOUT
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail
|
||||
{
|
||||
|
@ -315,7 +315,7 @@ namespace TINK.ViewModel.Map
|
|||
// Update map page filter
|
||||
ActiveFilterMap = TinkApp.GroupFilterMapPage;
|
||||
|
||||
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
|
||||
ActionText = AppResources.ActivityTextRequestingLocationPermissions;
|
||||
|
||||
// Check location permission
|
||||
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
|
||||
|
@ -325,13 +325,13 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
|
||||
|
||||
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageCenterMapLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageCenterMapLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (dialogResult)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ using TINK.Model.Bikes.Bike.BC;
|
|||
using TINK.Repository;
|
||||
using System.Net;
|
||||
using TINK.MultilingualResources;
|
||||
using System.Linq;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
|
@ -59,9 +60,9 @@ namespace TINK.ViewModel
|
|||
/// </summary>
|
||||
/// <param name="p_oStation">Station to get id from</param>
|
||||
/// <returns></returns>
|
||||
public static int GetStationId(string p_strStationName)
|
||||
public static int GetStationId(string stationName)
|
||||
{
|
||||
return int.Parse(p_strStationName.Replace(USER_FIENDLY_STATIONNUMBER_PREFIX, "").Trim());
|
||||
return int.Parse(stationName.Replace(USER_FIENDLY_STATIONNUMBER_PREFIX, "").Trim());
|
||||
}
|
||||
|
||||
/// <summary> Get full display name of a bike which includes id. </summary>
|
||||
|
@ -91,11 +92,11 @@ namespace TINK.ViewModel
|
|||
/// <summary>
|
||||
/// Maps state to color.
|
||||
/// </summary>
|
||||
/// <param name="p_eState"></param>
|
||||
/// <param name="state"></param>
|
||||
/// <returns></returns>
|
||||
public static Color GetColor(this InUseStateEnum p_eState)
|
||||
public static Color GetColor(this InUseStateEnum state)
|
||||
{
|
||||
switch (p_eState)
|
||||
switch (state)
|
||||
{
|
||||
case InUseStateEnum.Disposable:
|
||||
return Color.Default;
|
||||
|
@ -158,33 +159,48 @@ namespace TINK.ViewModel
|
|||
|
||||
}
|
||||
|
||||
/// <summary> Gets message that logged in user has not booked any bikes. </summary>
|
||||
public static FormattedString GetErrorInfoText(this Exception p_oException)
|
||||
/// <summary> Gets error message and handles aggegate exceptions. </summary>
|
||||
public static string GetErrorMessage(this Exception exception)
|
||||
{
|
||||
if (p_oException == null)
|
||||
if (exception == null)
|
||||
return string.Empty;
|
||||
|
||||
if (!(exception is AggregateException aggregateException))
|
||||
return exception.Message;
|
||||
|
||||
if (aggregateException.InnerExceptions.Count == 1)
|
||||
return aggregateException.InnerExceptions[0].Message;
|
||||
|
||||
return new AggregateException().Message + "\r\n"+ string.Join("\r\n", aggregateException.InnerExceptions.Select(x => x.Message));
|
||||
}
|
||||
|
||||
/// <summary> Gets message that logged in user has not booked any bikes. </summary>
|
||||
public static FormattedString GetErrorInfoText(this Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
FormattedString l_oError;
|
||||
// An error occurred getting bikes information.
|
||||
if (p_oException is WebConnectFailureException)
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
|
||||
l_oError = new FormattedString();
|
||||
l_oError.Spans.Add(new Span { Text = "Information!\r\n", FontAttributes = FontAttributes.Bold });
|
||||
l_oError.Spans.Add(new Span { Text = $"{p_oException.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" });
|
||||
l_oError.Spans.Add(new Span { Text = $"{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" });
|
||||
return l_oError;
|
||||
|
||||
}
|
||||
else if (p_oException is InvalidResponseException)
|
||||
else if (exception is InvalidResponseException)
|
||||
{
|
||||
l_oError = new FormattedString();
|
||||
l_oError.Spans.Add(new Span { Text = "Fehler, ungültige Serverantwort!\r\n", FontAttributes = FontAttributes.Bold });
|
||||
l_oError.Spans.Add(new Span { Text = $"{p_oException.Message}" });
|
||||
l_oError.Spans.Add(new Span { Text = $"{exception.Message}" });
|
||||
return l_oError;
|
||||
}
|
||||
else if (p_oException is WebForbiddenException)
|
||||
else if (exception is WebForbiddenException)
|
||||
{
|
||||
l_oError = new FormattedString();
|
||||
l_oError.Spans.Add(new Span { Text = "Beschäftigt... Einen Moment bitte!" });
|
||||
|
@ -193,37 +209,37 @@ namespace TINK.ViewModel
|
|||
|
||||
l_oError = new FormattedString();
|
||||
l_oError.Spans.Add(new Span { Text = "Allgemeiner Fehler!\r\n", FontAttributes = FontAttributes.Bold });
|
||||
l_oError.Spans.Add(new Span { Text = $"{p_oException}" });
|
||||
l_oError.Spans.Add(new Span { Text = $"{exception}" });
|
||||
return l_oError;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> User tabbed a URI. </summary>
|
||||
/// <param name="p_oSender">Sender of the event.</param>
|
||||
/// <param name="p_eEventArgs">Event arguments</param>
|
||||
public static void OnNavigating(object p_oSender, WebNavigatingEventArgs p_eEventArgs)
|
||||
/// <param name="sender">Sender of the event.</param>
|
||||
/// <param name="eventArgs">Event arguments</param>
|
||||
public static void OnNavigating(object sender, WebNavigatingEventArgs eventArgs)
|
||||
{
|
||||
|
||||
if (!p_eEventArgs.Url.ToUpper().StartsWith("HTTP"))
|
||||
if (!eventArgs.Url.ToUpper().StartsWith("HTTP"))
|
||||
{
|
||||
// An internal link was detected.
|
||||
// Stay inside WebView
|
||||
p_eEventArgs.Cancel = false;
|
||||
eventArgs.Cancel = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not navigate outside the document.
|
||||
p_eEventArgs.Cancel = true;
|
||||
eventArgs.Cancel = true;
|
||||
|
||||
DependencyService.Get<IExternalBrowserService>().OpenUrl(p_eEventArgs.Url);
|
||||
DependencyService.Get<IExternalBrowserService>().OpenUrl(eventArgs.Url);
|
||||
}
|
||||
|
||||
/// <summary> Gets the user group if a user friendly name.</summary>
|
||||
/// <param name="p_oUser"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUserGroupDisplayName(this User p_oUser)
|
||||
public static string GetUserGroupDisplayName(this User user)
|
||||
{
|
||||
return string.Join(" & ", p_oUser.Group);
|
||||
return string.Join(" & ", user.Group);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using TINK.Model.Bikes.Bike.BC;
|
||||
using TINK.ViewModel;
|
||||
|
||||
|
@ -91,5 +92,37 @@ namespace TestShareeLib.ViewModel
|
|||
bike.GetDisplayId(),
|
||||
Is.EqualTo(""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetErrorMessage_Null()
|
||||
{
|
||||
Assert.That(
|
||||
ViewModelHelper.GetErrorMessage(null),
|
||||
Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetErrorMessage_Ex()
|
||||
{
|
||||
Assert.That(
|
||||
new Exception("Ja toll").GetErrorMessage(),
|
||||
Is.EqualTo("Ja toll"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetErrorMessage_Aggregate_One()
|
||||
{
|
||||
Assert.That(
|
||||
new AggregateException(new Exception("Oh yes")).GetErrorMessage(),
|
||||
Is.EqualTo("Oh yes"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetErrorMessage_Aggregate_Two()
|
||||
{
|
||||
Assert.That(
|
||||
new AggregateException(new Exception("Oh yes"), new Exception("Oh no")).GetErrorMessage(),
|
||||
Is.EqualTo("One or more errors occurred.\r\nOh yes\r\nOh no"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ using TINK.Repository.Response;
|
|||
using Newtonsoft.Json;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using System.Threading;
|
||||
|
||||
namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -84,7 +85,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(false));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(false));
|
||||
|
||||
var subsequent = handler.HandleRequestOption1().Result;
|
||||
|
||||
|
@ -94,7 +95,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
Received.InOrder(() =>
|
||||
{
|
||||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein");
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No");
|
||||
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
||||
});
|
||||
|
||||
|
@ -136,7 +137,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Disconnected); // Simulate bike out of reach.
|
||||
|
||||
|
@ -150,7 +151,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x == null));
|
||||
bikesViewModel.ActionText = "Disconnecting lock...";
|
||||
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
||||
|
@ -198,11 +199,11 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown.
|
||||
|
||||
geolocation.GetAsync(Arg.Any<DateTime>()).Returns(Task.FromResult(
|
||||
geolocation.GetAsync(Arg.Any< CancellationToken?>(), Arg.Any<DateTime>()).Returns(Task.FromResult(
|
||||
new Xamarin.Essentials.Location(7, 9)
|
||||
));
|
||||
|
||||
|
@ -215,11 +216,11 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
Received.InOrder(() =>
|
||||
{
|
||||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime>());
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
geolocation.GetAsync(Arg.Any<DateTime>());
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x != null));
|
||||
bikesViewModel.ActionText = "Disconnecting lock...";
|
||||
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
||||
|
@ -267,11 +268,11 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown.
|
||||
|
||||
geolocation.GetAsync(Arg.Any<DateTime>()).Returns<Xamarin.Essentials.Location>(x => throw new Exception());
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime>()).Returns<Xamarin.Essentials.Location>(x => throw new Exception());
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object.
|
||||
bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object.
|
||||
|
@ -282,11 +283,11 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
Received.InOrder(() =>
|
||||
{
|
||||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime>());
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
geolocation.GetAsync(Arg.Any<DateTime>());
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x == null));
|
||||
bikesViewModel.ActionText = "Disconnecting lock...";
|
||||
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
||||
|
@ -334,7 +335,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown.
|
||||
|
||||
|
@ -355,10 +356,14 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Verbingungsfehler beim Zurückgeben des Rads!", "Internet muss erreichbar sein zum Zurückgeben des Rads.\r\nContext info\r\nIst WLAN verfügbar/ Mobilfunknetz vefügbar und mobile Daten aktiviert / ... ?", "OK");
|
||||
viewService.DisplayAdvancedAlert(
|
||||
"Connection error when returning the bike!",
|
||||
"Internet must be available when returning the bike.\r\nIst WLAN verfügbar/ Mobilfunknetz vefügbar und mobile Daten aktiviert / ... ?",
|
||||
"Context info",
|
||||
"OK");
|
||||
bikesViewModel.ActionText = "Updating...";
|
||||
pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again
|
||||
bikesViewModel.ActionText = "";
|
||||
|
@ -403,7 +408,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown.
|
||||
|
||||
|
@ -425,7 +430,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Error returning bike!", "Returning bike outside of station is not possible. Distance to station 42 is 15986 m.", "OK");
|
||||
|
@ -473,7 +478,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown.
|
||||
|
||||
|
@ -495,7 +500,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Error returning bike!", "Returning bike at an unknown location is not possible.\r\nBike can be returned if\r\n- location information is available when closing lock\r\n- bike is in reach and location information is available when pressing button \"Return bike\"", "OK");
|
||||
|
@ -543,7 +548,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown.
|
||||
|
||||
|
@ -563,7 +568,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAdvancedAlert("Statusfehler beim Zurückgeben des Rads!", "Outer message.", "Some invalid data received!", "OK");
|
||||
|
@ -611,7 +616,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown.
|
||||
|
||||
|
@ -630,7 +635,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Fehler beim Zurückgeben des Rads!", "Exception message.", "OK");
|
||||
|
|
|
@ -22,6 +22,7 @@ using Newtonsoft.Json;
|
|||
using TINK.Repository.Response;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using System.Threading;
|
||||
|
||||
namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -84,7 +85,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 Allround Mono abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(false));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0 Allround Mono?", "Yes", "No").Returns(Task.FromResult(false));
|
||||
|
||||
var subsequent = handler.HandleRequestOption1().Result;
|
||||
|
||||
|
@ -134,12 +135,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success
|
||||
|
||||
geolocation.GetAsync(Arg.Any<DateTime>()).Returns(Task.FromResult(new Location(1, 2)));
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime>()).Returns(Task.FromResult(new Location(1, 2)));
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Disposable); // Return call leads to setting of state to disposable.
|
||||
|
||||
|
@ -149,13 +150,13 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
Received.InOrder(() =>
|
||||
{
|
||||
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime>()); // Geolocation must be retrieved
|
||||
bikesViewModel.ActionText = "One moment please...";
|
||||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Closing lock...";
|
||||
locks.Received()[0].CloseAsync(); // Lock must be closed
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
geolocation.GetAsync(Arg.Any<DateTime>()); // Geolocation must be retrieved
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 1 && x.Longitude ==2), Arg.Any<ISmartDevice>()); // Booking must be performed
|
||||
bikesViewModel.ActionText = "Disconnecting lock...";
|
||||
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
||||
|
@ -203,7 +204,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns< LockitLockingState?>(x => throw new OutOfReachException());
|
||||
|
@ -269,7 +270,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns<LockitLockingState?>(x => throw new System.Exception("Blu"));
|
||||
|
@ -335,7 +336,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(LockitLockingState.Open);
|
||||
|
@ -374,7 +375,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
/// Final state: Booked locked.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCloseAndReturnGetGeolocationFailsException()
|
||||
public async Task TestCloseAndReturnGetGeolocationFailsException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
|
@ -400,18 +401,18 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(LockitLockingState.Closed);
|
||||
|
||||
geolocation.GetAsync(Arg.Any<DateTime>()).Returns<Location>(x => throw new System.Exception("noloc"));
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime>()).Returns(Task.FromException<Location>(new Exception("noloc")));
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails.
|
||||
|
||||
var subsequent = handler.HandleRequestOption1().Result;
|
||||
var subsequent = await handler.HandleRequestOption1();
|
||||
|
||||
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
||||
await locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
||||
|
||||
// Verify behaviour
|
||||
Received.InOrder(() =>
|
||||
|
@ -421,9 +422,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Closing lock...";
|
||||
locks.Received()[0].CloseAsync(); // Lock must be closed
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Fehler bei Standortabfrage!", "Schloss schließen und Miete beenden ist nicht möglich.\r\nnoloc", "OK");
|
||||
viewService.DisplayAdvancedAlert("Error Query Location!", "Closing the lock and ending the rental is not possible.", "noloc", "OK");
|
||||
bikesViewModel.ActionText = "Updating...";
|
||||
pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again
|
||||
bikesViewModel.ActionText = "";
|
||||
|
@ -468,7 +469,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(LockitLockingState.Closed);
|
||||
|
@ -489,10 +490,14 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Closing lock...";
|
||||
locks.Received()[0].CloseAsync(); // Lock must be closed
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Verbingungsfehler beim Zurückgeben des Rads!", "Internet muss erreichbar sein beim Zurückgeben des Rads.\r\nContext info\r\nIst WLAN verfügbar/ Mobilfunknetz vefügbar und mobile Daten aktiviert / ... ?", "OK");
|
||||
viewService.DisplayAdvancedAlert(
|
||||
"Connection error when returning the bike!",
|
||||
"Internet must be available when returning the bike.\r\nIst WLAN verfügbar/ Mobilfunknetz vefügbar und mobile Daten aktiviert / ... ?",
|
||||
"Context info",
|
||||
"OK");
|
||||
bikesViewModel.ActionText = "Updating...";
|
||||
pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again
|
||||
bikesViewModel.ActionText = "";
|
||||
|
@ -537,7 +542,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(LockitLockingState.Closed);
|
||||
|
@ -558,8 +563,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Closing lock...";
|
||||
locks.Received()[0].CloseAsync(); // Lock must be closed
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Fehler beim Zurückgeben des Rads!", "Exception message.", "OK");
|
||||
bikesViewModel.ActionText = "Updating...";
|
||||
|
@ -606,7 +611,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(LockitLockingState.Closed);
|
||||
|
@ -630,8 +635,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Closing lock...";
|
||||
locks.Received()[0].CloseAsync(); // Lock must be closed
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Error returning bike!", "Returning bike outside of station is not possible. Distance to station 77 is 15986 m.", "OK");
|
||||
bikesViewModel.ActionText = "Updating...";
|
||||
|
@ -678,7 +683,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(LockitLockingState.Closed);
|
||||
|
@ -702,8 +707,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Closing lock...";
|
||||
locks.Received()[0].CloseAsync(); // Lock must be closed
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAlert("Error returning bike!", "Returning bike at an unknown location is not possible.\r\nBike can only be returned if bike is in reach and location information is available.", "OK");
|
||||
bikesViewModel.ActionText = "Updating...";
|
||||
|
@ -750,7 +755,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
|
||||
bike.Id.Returns("0");
|
||||
|
||||
viewService.DisplayAlert(string.Empty, "Fahrrad Nr. 0 abschließen und zurückgeben?", "Ja", "Nein").Returns(Task.FromResult(true));
|
||||
viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
||||
|
||||
locks[0].CloseAsync()
|
||||
.Returns(LockitLockingState.Closed);
|
||||
|
@ -772,8 +777,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
|
|||
pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action
|
||||
bikesViewModel.ActionText = "Closing lock...";
|
||||
locks.Received()[0].CloseAsync(); // Lock must be closed
|
||||
bikesViewModel.ActionText = "Abfrage Standort...";
|
||||
bikesViewModel.ActionText = "Gebe Rad zurück...";
|
||||
bikesViewModel.ActionText = "Query location...";
|
||||
bikesViewModel.ActionText = "Returning bike...";
|
||||
bikesViewModel.ActionText = "";
|
||||
viewService.DisplayAdvancedAlert("Statusfehler beim Zurückgeben des Rads!", "Outer message.", "Some invalid data received!", "OK");
|
||||
bikesViewModel.ActionText = "Updating...";
|
||||
|
|
|
@ -487,7 +487,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel
|
|||
"OK");
|
||||
});
|
||||
|
||||
Assert.IsEmpty(myBikes.StatusInfoText);
|
||||
Assert.That(
|
||||
myBikes.StatusInfoText,
|
||||
Is.Empty,
|
||||
"Status info text must be empty.");
|
||||
|
||||
Assert.AreEqual(2, myBikes.Count);
|
||||
Assert.IsTrue(myBikes.IsIdle);
|
||||
|
@ -501,7 +504,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel
|
|||
Assert.AreEqual("Search lock", bike1537.LockitButtonText);
|
||||
|
||||
Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible);
|
||||
Assert.IsEmpty(myBikes.NoBikesOccupiedText);
|
||||
Assert.That(
|
||||
myBikes.NoBikesOccupiedText,
|
||||
Is.Empty,
|
||||
"Label which informs that no bikes are reserved/ rented must be empty.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Services.Geolocation;
|
||||
using Xamarin.Essentials;
|
||||
|
@ -8,7 +9,7 @@ namespace TestTINKLib.Mocks.Services
|
|||
public class GeolocationMock : IGeolocation
|
||||
{
|
||||
|
||||
public Task<Location> GetAsync(DateTime? timeStamp = null)
|
||||
public Task<Location> GetAsync(CancellationToken? cancelToken = null, DateTime? timeStamp = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -32,10 +32,6 @@
|
|||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Arendi.BleLibrary, Version=4.5.1.999, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\LockItArendi\Arendi\Arendi.BleLibrary.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
|
|
Loading…
Reference in a new issue