mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-01-21 12:04:25 +01:00
Version 3.0.294
This commit is contained in:
parent
d92fb4a40f
commit
8f40f2c208
133 changed files with 17890 additions and 14246 deletions
|
@ -16,7 +16,7 @@
|
|||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
|
||||
<AndroidStoreUncompressedFileExtensions />
|
||||
<MandroidI18n />
|
||||
<JavaMaximumHeapSize>2G</JavaMaximumHeapSize>
|
||||
|
@ -47,6 +47,7 @@
|
|||
<AndroidPackageFormat>aab</AndroidPackageFormat>
|
||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||
<AndroidCreatePackagePerAbi>false</AndroidCreatePackagePerAbi>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
@ -66,16 +67,17 @@
|
|||
<AndroidPackageFormat>aab</AndroidPackageFormat>
|
||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||
<AndroidCreatePackagePerAbi>false</AndroidCreatePackagePerAbi>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="MonkeyCache">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MonkeyCache.FileStore">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
@ -142,7 +144,7 @@
|
|||
<PackageReference Include="System.Xml.ReaderWriter" Version="4.3.1" />
|
||||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Validation" Version="2.5.42" />
|
||||
<PackageReference Include="Validation" Version="2.5.51" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity">
|
||||
<Version>3.2.0</Version>
|
||||
</PackageReference>
|
||||
|
@ -168,37 +170,37 @@
|
|||
<PackageReference Include="Xamarin.Android.Support.v7.RecyclerView" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Vector.Drawable" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Core">
|
||||
<Version>1.6.0.3</Version>
|
||||
<Version>1.7.0.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter">
|
||||
<Version>1.2.5.2</Version>
|
||||
<Version>1.2.6.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.Palette">
|
||||
<Version>1.0.0.10</Version>
|
||||
<Version>1.0.0.13</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.RecyclerView">
|
||||
<Version>1.2.1.3</Version>
|
||||
<Version>1.2.1.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Auth" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.10.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.11.0" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit">
|
||||
<Version>1.3.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.0</Version>
|
||||
<Version>1.7.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2196" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2401" />
|
||||
<PackageReference Include="Xamarin.Forms.AppLinks">
|
||||
<Version>5.0.0.2244</Version>
|
||||
<Version>5.0.0.2401</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps">
|
||||
<Version>3.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps.Bindings" Version="3.0.0" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="117.6.0.2" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.1.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="117.2.1.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0.5" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="117.6.0.6" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.1.5" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="117.2.1.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.TeilRad.LastenradBayern" android:versionName="3.0.290" android:versionCode="290">
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="30" />
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.TeilRad.LastenradBayern" android:versionName="3.0.294" android:versionCode="294">
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
|
||||
<!-- Google Maps related permissions -->
|
||||
<!-- Permission to receive remote notifications from Google Play Services -->
|
||||
<!-- Notice here that we have the package name of our application as a prefix on the permissions. -->
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
@ -12,7 +12,7 @@ using Xamarin.Forms;
|
|||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("LastenradBayern.Android")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
9185
LastenradBayern/TINK.Android/Resources/Resource.Designer.cs
generated
9185
LastenradBayern/TINK.Android/Resources/Resource.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -53,8 +53,8 @@
|
|||
<key>CFBundleDisplayName</key>
|
||||
<string>LastenradBayern</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>290</string>
|
||||
<string>294</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.290</string>
|
||||
<string>3.0.294</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -113,13 +113,13 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bcl.Build" Version="1.0.21" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="MonkeyCache">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MonkeyCache.FileStore">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
@ -182,18 +182,18 @@
|
|||
<PackageReference Include="System.Xml.ReaderWriter" Version="4.3.1" />
|
||||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Validation" Version="2.5.42" />
|
||||
<PackageReference Include="Validation" Version="2.5.51" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity">
|
||||
<Version>3.2.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xam.Plugins.Messaging" Version="5.2.0" />
|
||||
<PackageReference Include="Xamarin.Auth" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.10.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.11.0" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit">
|
||||
<Version>1.3.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.0</Version>
|
||||
<Version>1.7.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps">
|
||||
<Version>3.3.0</Version>
|
||||
|
@ -213,7 +213,7 @@
|
|||
<Version>0.7.104</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms">
|
||||
<Version>5.0.0.2196</Version>
|
||||
<Version>5.0.0.2401</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<!-- Add more resources here -->
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- Add more resource dictionaries here -->
|
||||
<themes:ShareeBike/>
|
||||
<themes:LastenradBayern/>
|
||||
<!-- Add more resource dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!-- Add more resources here -->
|
||||
|
|
|
@ -17,10 +17,9 @@ namespace TINK.View.Bike
|
|||
}
|
||||
|
||||
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
|
||||
{
|
||||
return item is TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel
|
||||
? iLockIBike
|
||||
: bCBike;
|
||||
}
|
||||
=> item is TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel ||
|
||||
item is TINK.ViewModel.Bikes.Bike.CopriLock.BikeViewModel
|
||||
? iLockIBike
|
||||
: bCBike;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ namespace TINK.View.BikesAtStation
|
|||
// No need to create view model, set binding context an items source if already done.
|
||||
// If done twice tap events are fired multiple times (when hiding page using home button).
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -112,6 +113,7 @@ namespace TINK.View.BikesAtStation
|
|||
{
|
||||
Log.ForContext<BikesAtStationPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
|
||||
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -126,6 +128,7 @@ namespace TINK.View.BikesAtStation
|
|||
BikesAtStationListView.ItemsSource = m_oViewModel;
|
||||
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -9,7 +9,6 @@ using Xamarin.Forms.Xaml;
|
|||
namespace TINK.View.Map
|
||||
{
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.ViewModel.Map;
|
||||
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
|
@ -153,6 +152,7 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Constructing map page view model failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,7 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -188,6 +189,7 @@ namespace TINK.View.Map
|
|||
{
|
||||
// Continue because styling is not essential.
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -206,10 +208,13 @@ namespace TINK.View.Map
|
|||
{
|
||||
Log.ForContext<MapPage>().Verbose("Invoking OnAppearing on map page view model.");
|
||||
await MapPageViewModel.OnAppearing();
|
||||
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,13 +40,14 @@ namespace TINK.View.MyBikes
|
|||
{
|
||||
// Don't repeat the initialization if it has been completed already.
|
||||
if (isInitializationStarted) return;
|
||||
isInitializationStarted = true;
|
||||
isInitializationStarted = true;
|
||||
|
||||
if (m_oViewModel != null)
|
||||
{
|
||||
// No need to create view model, set binding context an items source if already done.
|
||||
// If done twice tap events are fired multiple times (when hiding page using home button).
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -80,8 +81,8 @@ namespace TINK.View.MyBikes
|
|||
{
|
||||
Log.ForContext<MyBikesPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
|
||||
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
|
@ -90,6 +91,7 @@ namespace TINK.View.MyBikes
|
|||
MyBikesListView.ItemsSource = m_oViewModel;
|
||||
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Plugin.BLE" Version="2.1.2" />
|
||||
<PackageReference Include="Polly" Version="7.2.2" />
|
||||
<PackageReference Include="Polly" Version="7.2.3" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace TINK.Services.BluetoothLock.BLE
|
|||
/// <summary> Lenght of seed in bytes.</summary>
|
||||
private const int SEEDLENGTH = 16;
|
||||
|
||||
|
||||
/// <summary> Timeout for open/ close operations.</summary>
|
||||
protected const int OPEN_CLOSE_TIMEOUT_MS = 30000;
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ namespace TINK.Services.BluetoothLock.BLE
|
|||
|
||||
case LockitLockingState.CouldntCloseMoving:
|
||||
// Expected error. ILockIt could not be closed (bike is moving)
|
||||
throw new CounldntCloseMovingException();
|
||||
throw new CouldntCloseMovingException();
|
||||
|
||||
case LockitLockingState.Closed:
|
||||
return lockingState;
|
||||
|
|
|
@ -200,7 +200,7 @@ namespace TINK.Services.BluetoothLock.BLE
|
|||
|
||||
case LockitLockingState.CouldntCloseMoving:
|
||||
// Expected error. ILockIt could not be closed (bike is moving)
|
||||
throw new CounldntCloseMovingException();
|
||||
throw new CouldntCloseMovingException();
|
||||
|
||||
case LockitLockingState.Closed:
|
||||
// Everything is ok.
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
<Warning Text="$(MSBuildProjectFile) is Multilingual build enabled, but the Multilingual App Toolkit is unavailable during the build. If building with Visual Studio, please check to ensure that toolkit is properly installed." />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Folder Include="Model\Bikes\Bike\BluetoothLock\" />
|
||||
<Folder Include="Model\Bikes\Bike\" />
|
||||
<Folder Include="Model\Device\" />
|
||||
<Folder Include="Services\BluetoothLock\Crypto\" />
|
||||
<Folder Include="Services\BluetoothLock\Tdo\" />
|
||||
|
|
8
LockItShared/Model/Bikes/Bike/CopriLock/ILockInfo.cs
Normal file
8
LockItShared/Model/Bikes/Bike/CopriLock/ILockInfo.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace TINK.Model.Bikes.Bike.CopriLock
|
||||
{
|
||||
public interface ILockInfo
|
||||
{
|
||||
/// <summary> Locking state of backend lock. </summary>
|
||||
LockingState State { get; }
|
||||
}
|
||||
}
|
88
LockItShared/Model/Bikes/Bike/CopriLock/LockInfo.cs
Normal file
88
LockItShared/Model/Bikes/Bike/CopriLock/LockInfo.cs
Normal file
|
@ -0,0 +1,88 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TINK.Model.Bikes.Bike.CopriLock
|
||||
{
|
||||
/// <summary> Locking states. </summary>
|
||||
public enum LockingState
|
||||
{
|
||||
/// <summary> App is not connected to lock.</summary>
|
||||
UnknownDisconnected,
|
||||
|
||||
/// <summary> Lock is closed. </summary>
|
||||
Closed,
|
||||
|
||||
/// <summary> Lock is closing. </summary>
|
||||
Closing,
|
||||
|
||||
/// <summary> Lock is open. </summary>
|
||||
Open,
|
||||
|
||||
/// <summary> Lock is opening. </summary>
|
||||
Opening
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class LockInfo : ILockInfo
|
||||
{
|
||||
/// <summary> Locking state of bluetooth lock. </summary>
|
||||
[DataMember]
|
||||
public LockingState State { get; private set; } = LockingState.UnknownDisconnected;
|
||||
|
||||
public override bool Equals(object obj) => this.Equals(obj as LockInfo);
|
||||
|
||||
public bool Equals(LockInfo other)
|
||||
{
|
||||
if (Object.ReferenceEquals(other, null)) return false;
|
||||
if (Object.ReferenceEquals(this, other)) return true;
|
||||
if (this.GetType() != other.GetType()) return false;
|
||||
|
||||
return ToString() == other.ToString();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ToString().GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonConvert.SerializeObject(this);
|
||||
}
|
||||
|
||||
public static bool operator ==(LockInfo lhs, LockInfo rhs)
|
||||
{
|
||||
if (Object.ReferenceEquals(lhs, null))
|
||||
return Object.ReferenceEquals(rhs, null) ? true /*null == null = true*/: false;
|
||||
|
||||
return lhs.Equals(rhs);
|
||||
}
|
||||
|
||||
public static bool operator !=(LockInfo lhs, LockInfo rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
public class Builder
|
||||
{
|
||||
public Builder(LockInfo lockInfo = null)
|
||||
{
|
||||
if (lockInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LockInfo = JsonConvert.DeserializeObject<LockInfo>(JsonConvert.SerializeObject(lockInfo));
|
||||
}
|
||||
|
||||
private readonly LockInfo LockInfo = new LockInfo();
|
||||
|
||||
|
||||
public LockingState State { get => LockInfo.State; set => LockInfo.State = value; }
|
||||
|
||||
public LockInfo Build() => LockInfo;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
|
||||
namespace TINK.Services.BluetoothLock.Exception
|
||||
{
|
||||
public class CounldntCloseMovingException : StateAwareException
|
||||
public class CouldntCloseMovingException : StateAwareException
|
||||
{
|
||||
public CounldntCloseMovingException() : base(
|
||||
public CouldntCloseMovingException() : base(
|
||||
LockingState.Open, // Locking bold is probable (according to haveltec) still open.
|
||||
MultilingualResources.Resources.ErrorCloseLockBikeMoving)
|
||||
{
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
namespace TINK.Services.CopriLock.Exception
|
||||
{
|
||||
public class CouldntCloseBoldBlockedException : System.Exception
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
namespace TINK.Services.CopriLock.Exception
|
||||
{
|
||||
public class CouldntOpenBoldIsBlockedException : System.Exception
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
namespace TINK.Services.CopriLock.Exception
|
||||
{
|
||||
public class CouldntOpenBoldWasBlockedException : System.Exception
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
namespace TINK.Services.CopriLock.Exception
|
||||
{
|
||||
public class CounldntCloseMovingException : System.Exception
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
namespace TINK.Services.CopriLock.Exception
|
||||
{
|
||||
public class OutOfReachException : System.Exception
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
|
||||
namespace TINK.Services.CopriLock.Exception
|
||||
{
|
||||
public abstract class StateAwareException : System.Exception
|
||||
{
|
||||
public StateAwareException(LockingState state, string description) : base(description)
|
||||
{
|
||||
State = state;
|
||||
}
|
||||
|
||||
/// <summary> Holds the state reported by lock.</summary>
|
||||
public LockingState State { get; }
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
|
||||
<AndroidStoreUncompressedFileExtensions />
|
||||
<MandroidI18n />
|
||||
<JavaMaximumHeapSize>2G</JavaMaximumHeapSize>
|
||||
|
@ -47,6 +47,7 @@
|
|||
<AndroidPackageFormat>aab</AndroidPackageFormat>
|
||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||
<AndroidCreatePackagePerAbi>false</AndroidCreatePackagePerAbi>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
@ -66,16 +67,17 @@
|
|||
<AndroidPackageFormat>aab</AndroidPackageFormat>
|
||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||
<AndroidCreatePackagePerAbi>false</AndroidCreatePackagePerAbi>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="MonkeyCache">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MonkeyCache.FileStore">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
@ -142,7 +144,7 @@
|
|||
<PackageReference Include="System.Xml.ReaderWriter" Version="4.3.1" />
|
||||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Validation" Version="2.5.42" />
|
||||
<PackageReference Include="Validation" Version="2.5.51" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity">
|
||||
<Version>3.2.0</Version>
|
||||
</PackageReference>
|
||||
|
@ -168,37 +170,37 @@
|
|||
<PackageReference Include="Xamarin.Android.Support.v7.RecyclerView" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Vector.Drawable" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Core">
|
||||
<Version>1.6.0.3</Version>
|
||||
<Version>1.7.0.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter">
|
||||
<Version>1.2.5.2</Version>
|
||||
<Version>1.2.6.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.Palette">
|
||||
<Version>1.0.0.10</Version>
|
||||
<Version>1.0.0.13</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.RecyclerView">
|
||||
<Version>1.2.1.3</Version>
|
||||
<Version>1.2.1.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Auth" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.10.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.11.0" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit">
|
||||
<Version>1.3.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.0</Version>
|
||||
<Version>1.7.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2196" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2401" />
|
||||
<PackageReference Include="Xamarin.Forms.AppLinks">
|
||||
<Version>5.0.0.2244</Version>
|
||||
<Version>5.0.0.2401</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps">
|
||||
<Version>3.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps.Bindings" Version="3.0.0" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="117.6.0.2" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.1.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="117.2.1.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0.5" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="117.6.0.6" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.1.5" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="117.2.1.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.TeilRad.Meinkonrad" android:versionName="3.0.290" android:versionCode="290">
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="30" />
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.TeilRad.Meinkonrad" android:versionName="3.0.294" android:versionCode="294">
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
|
||||
<!-- Google Maps related permissions -->
|
||||
<!-- Permission to receive remote notifications from Google Play Services -->
|
||||
<!-- Notice here that we have the package name of our application as a prefix on the permissions. -->
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
@ -12,7 +12,7 @@ using Xamarin.Forms;
|
|||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Meinkonrad.Android")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
9185
Meinkonrad/TINK.Android/Resources/Resource.Designer.cs
generated
9185
Meinkonrad/TINK.Android/Resources/Resource.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -53,8 +53,8 @@
|
|||
<key>CFBundleDisplayName</key>
|
||||
<string>Mein konrad</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>290</string>
|
||||
<string>294</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.290</string>
|
||||
<string>3.0.294</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -113,13 +113,13 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bcl.Build" Version="1.0.21" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="MonkeyCache">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MonkeyCache.FileStore">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
@ -182,18 +182,18 @@
|
|||
<PackageReference Include="System.Xml.ReaderWriter" Version="4.3.1" />
|
||||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Validation" Version="2.5.42" />
|
||||
<PackageReference Include="Validation" Version="2.5.51" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity">
|
||||
<Version>3.2.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xam.Plugins.Messaging" Version="5.2.0" />
|
||||
<PackageReference Include="Xamarin.Auth" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.10.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.11.0" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit">
|
||||
<Version>1.3.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.0</Version>
|
||||
<Version>1.7.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps">
|
||||
<Version>3.3.0</Version>
|
||||
|
@ -213,7 +213,7 @@
|
|||
<Version>0.7.104</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms">
|
||||
<Version>5.0.0.2196</Version>
|
||||
<Version>5.0.0.2401</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1608,12 +1608,12 @@
|
|||
<Folder Include="Media.xcassets\30_Green.imageset\" />
|
||||
<Folder Include="Media.xcassets\30_LightBlue.imageset\" />
|
||||
<Folder Include="Media.xcassets\30_Red.imageset\" />
|
||||
<Folder Include="Media.xcassets\konrad_nobg.imageset\" />
|
||||
<Folder Include="Media.xcassets\Open_Blue.imageset\" />
|
||||
<Folder Include="Media.xcassets\Open_Green.imageset\" />
|
||||
<Folder Include="Media.xcassets\Open_LightBlue.imageset\" />
|
||||
<Folder Include="Media.xcassets\Open_Red.imageset\" />
|
||||
<Folder Include="Media.xcassets\sharee_no_background.imageset\" />
|
||||
<Folder Include="Media.xcassets\konrad_nobg.imageset\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ITunesArtwork Include="iTunesArtwork" />
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<!-- Add more resources here -->
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- Add more resource dictionaries here -->
|
||||
<themes:ShareeBike/>
|
||||
<themes:Konrad/>
|
||||
<!-- Add more resource dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!-- Add more resources here -->
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace TINK.View.BikesAtStation
|
|||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.ViewModel;
|
||||
using TINK.Model;
|
||||
|
@ -33,6 +33,10 @@ using TINK.View.MasterDetail;
|
|||
{
|
||||
|
||||
private BikesAtStationPageViewModel m_oViewModel;
|
||||
|
||||
/// <summary> Initialization status to ensure initialization logic is not called multiple times. </summary>
|
||||
private bool isInitializationStarted = false;
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
public BikesAtStationPage()
|
||||
{
|
||||
|
@ -62,6 +66,10 @@ using TINK.View.MasterDetail;
|
|||
/// </summary>
|
||||
protected async override void OnAppearing()
|
||||
{
|
||||
// Don't repeat the initialization if it has been completed already.
|
||||
if (isInitializationStarted) return;
|
||||
isInitializationStarted = true;
|
||||
|
||||
if (m_oViewModel != null)
|
||||
{
|
||||
#if BACKSTYLE
|
||||
|
@ -72,6 +80,7 @@ using TINK.View.MasterDetail;
|
|||
// No need to create view model, set binding context an items source if already done.
|
||||
// If done twice tap events are fired multiple times (when hiding page using home button).
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,6 +114,7 @@ using TINK.View.MasterDetail;
|
|||
{
|
||||
Log.ForContext<BikesAtStationPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
|
||||
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -119,6 +129,7 @@ using TINK.View.MasterDetail;
|
|||
BikesAtStationListView.ItemsSource = m_oViewModel;
|
||||
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -167,7 +178,7 @@ using TINK.View.MasterDetail;
|
|||
/// <param name="cancel">Text of button.</param>
|
||||
/// <returns>True if user pressed accept.</returns>
|
||||
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||
|
||||
/// <summary> Displays detailed alert message.</summary>
|
||||
/// <param name="title">Title of message.</param>
|
||||
|
@ -233,5 +244,5 @@ using TINK.View.MasterDetail;
|
|||
/// <returns>User feedback.</returns>
|
||||
public async Task<IUserFeedback> DisplayUserFeedbackPopup(string co2Saving = null) => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup(co2Saving));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -152,6 +152,7 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Constructing map page view model failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -166,6 +167,7 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -187,6 +189,7 @@ namespace TINK.View.Map
|
|||
{
|
||||
// Continue because styling is not essential.
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -205,10 +208,13 @@ namespace TINK.View.Map
|
|||
{
|
||||
Log.ForContext<MapPage>().Verbose("Invoking OnAppearing on map page view model.");
|
||||
await MapPageViewModel.OnAppearing();
|
||||
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ using Xamarin.Forms.Xaml;
|
|||
namespace TINK.View.MyBikes
|
||||
{
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Device;
|
||||
using TINK.ViewModel.MyBikes;
|
||||
using Xamarin.CommunityToolkit.Extensions;
|
||||
|
@ -39,13 +40,14 @@ namespace TINK.View.MyBikes
|
|||
{
|
||||
// Don't repeat the initialization if it has been completed already.
|
||||
if (isInitializationStarted) return;
|
||||
isInitializationStarted = true;
|
||||
isInitializationStarted = true;
|
||||
|
||||
if (m_oViewModel != null)
|
||||
{
|
||||
// No need to create view model, set binding context an items source if already done.
|
||||
// If done twice tap events are fired multiple times (when hiding page using home button).
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -79,8 +81,8 @@ namespace TINK.View.MyBikes
|
|||
{
|
||||
Log.ForContext<MyBikesPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
|
||||
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
|
@ -89,6 +91,7 @@ namespace TINK.View.MyBikes
|
|||
MyBikesListView.ItemsSource = m_oViewModel;
|
||||
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?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.290" android:versionCode="290">
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="30" />
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.294" android:versionCode="294">
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
|
||||
<!-- Google Maps related permissions -->
|
||||
<!-- Permission to receive remote notifications from Google Play Services -->
|
||||
<!-- Notice here that we have the package name of our application as a prefix on the permissions. -->
|
||||
|
|
|
@ -12,7 +12,7 @@ using Xamarin.Forms;
|
|||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("TINK.Android")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
9185
TINK/TINK.Android/Resources/Resource.Designer.cs
generated
9185
TINK/TINK.Android/Resources/Resource.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@
|
|||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
|
||||
<AndroidStoreUncompressedFileExtensions />
|
||||
<MandroidI18n />
|
||||
<JavaMaximumHeapSize>2G</JavaMaximumHeapSize>
|
||||
|
@ -44,6 +44,7 @@
|
|||
<BundleAssemblies>false</BundleAssemblies>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
@ -60,16 +61,17 @@
|
|||
<BundleAssemblies>false</BundleAssemblies>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||
<MandroidI18n />
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="MonkeyCache">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MonkeyCache.FileStore">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
@ -136,7 +138,7 @@
|
|||
<PackageReference Include="System.Xml.ReaderWriter" Version="4.3.1" />
|
||||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Validation" Version="2.5.42" />
|
||||
<PackageReference Include="Validation" Version="2.5.51" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity">
|
||||
<Version>3.2.0</Version>
|
||||
</PackageReference>
|
||||
|
@ -162,37 +164,37 @@
|
|||
<PackageReference Include="Xamarin.Android.Support.v7.RecyclerView" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Vector.Drawable" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Core">
|
||||
<Version>1.6.0.3</Version>
|
||||
<Version>1.7.0.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter">
|
||||
<Version>1.2.5.2</Version>
|
||||
<Version>1.2.6.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.Palette">
|
||||
<Version>1.0.0.10</Version>
|
||||
<Version>1.0.0.13</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.RecyclerView">
|
||||
<Version>1.2.1.3</Version>
|
||||
<Version>1.2.1.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Auth" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.10.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.11.0" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit">
|
||||
<Version>1.3.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.0</Version>
|
||||
<Version>1.7.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2196" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2401" />
|
||||
<PackageReference Include="Xamarin.Forms.AppLinks">
|
||||
<Version>5.0.0.2244</Version>
|
||||
<Version>5.0.0.2401</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps">
|
||||
<Version>3.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps.Bindings" Version="3.0.0" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="117.6.0.2" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.1.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="117.2.1.1" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0.5" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="117.6.0.6" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.1.5" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="117.2.1.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
<key>CFBundleDisplayName</key>
|
||||
<string>sharee.bike</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>290</string>
|
||||
<string>294</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.290</string>
|
||||
<string>3.0.294</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -113,13 +113,13 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bcl.Build" Version="1.0.21" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.NETCore.Platforms" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="MonkeyCache">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MonkeyCache.FileStore">
|
||||
<Version>1.5.2</Version>
|
||||
<Version>1.6.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
@ -182,18 +182,18 @@
|
|||
<PackageReference Include="System.Xml.ReaderWriter" Version="4.3.1" />
|
||||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Validation" Version="2.5.42" />
|
||||
<PackageReference Include="Validation" Version="2.5.51" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity">
|
||||
<Version>3.2.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xam.Plugins.Messaging" Version="5.2.0" />
|
||||
<PackageReference Include="Xamarin.Auth" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.10.0" />
|
||||
<PackageReference Include="Xamarin.Build.Download" Version="0.11.0" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit">
|
||||
<Version>1.3.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.0</Version>
|
||||
<Version>1.7.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps">
|
||||
<Version>3.3.0</Version>
|
||||
|
@ -213,7 +213,7 @@
|
|||
<Version>0.7.104</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms">
|
||||
<Version>5.0.0.2196</Version>
|
||||
<Version>5.0.0.2401</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace TINK.View.Bike
|
|||
|
||||
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
|
||||
{
|
||||
return item is TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel
|
||||
return item is ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel
|
||||
? iLockIBike
|
||||
: bCBike;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace TINK.View.BikesAtStation
|
|||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.ViewModel;
|
||||
using TINK.Model;
|
||||
|
@ -33,6 +33,10 @@ using TINK.View.MasterDetail;
|
|||
{
|
||||
|
||||
private BikesAtStationPageViewModel m_oViewModel;
|
||||
|
||||
/// <summary> Initialization status to ensure initialization logic is not called multiple times. </summary>
|
||||
private bool isInitializationStarted = false;
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
public BikesAtStationPage()
|
||||
{
|
||||
|
@ -62,6 +66,10 @@ using TINK.View.MasterDetail;
|
|||
/// </summary>
|
||||
protected async override void OnAppearing()
|
||||
{
|
||||
// Don't repeat the initialization if it has been completed already.
|
||||
if (isInitializationStarted) return;
|
||||
isInitializationStarted = true;
|
||||
|
||||
if (m_oViewModel != null)
|
||||
{
|
||||
#if BACKSTYLE
|
||||
|
@ -72,6 +80,7 @@ using TINK.View.MasterDetail;
|
|||
// No need to create view model, set binding context an items source if already done.
|
||||
// If done twice tap events are fired multiple times (when hiding page using home button).
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,6 +114,7 @@ using TINK.View.MasterDetail;
|
|||
{
|
||||
Log.ForContext<BikesAtStationPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
|
||||
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -119,6 +129,7 @@ using TINK.View.MasterDetail;
|
|||
BikesAtStationListView.ItemsSource = m_oViewModel;
|
||||
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -167,7 +178,7 @@ using TINK.View.MasterDetail;
|
|||
/// <param name="cancel">Text of button.</param>
|
||||
/// <returns>True if user pressed accept.</returns>
|
||||
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
|
||||
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||
|
||||
/// <summary> Displays detailed alert message.</summary>
|
||||
/// <param name="title">Title of message.</param>
|
||||
|
@ -233,5 +244,5 @@ using TINK.View.MasterDetail;
|
|||
/// <returns>User feedback.</returns>
|
||||
public async Task<IUserFeedback> DisplayUserFeedbackPopup(string co2Saving = null) => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup(co2Saving));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -152,6 +152,7 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Constructing map page view model failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -166,6 +167,7 @@ namespace TINK.View.Map
|
|||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -187,6 +189,7 @@ namespace TINK.View.Map
|
|||
{
|
||||
// Continue because styling is not essential.
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -205,10 +208,13 @@ namespace TINK.View.Map
|
|||
{
|
||||
Log.ForContext<MapPage>().Verbose("Invoking OnAppearing on map page view model.");
|
||||
await MapPageViewModel.OnAppearing();
|
||||
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<MapPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ using Xamarin.Forms.Xaml;
|
|||
namespace TINK.View.MyBikes
|
||||
{
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Device;
|
||||
using TINK.ViewModel.MyBikes;
|
||||
using Xamarin.CommunityToolkit.Extensions;
|
||||
|
@ -39,13 +40,14 @@ namespace TINK.View.MyBikes
|
|||
{
|
||||
// Don't repeat the initialization if it has been completed already.
|
||||
if (isInitializationStarted) return;
|
||||
isInitializationStarted = true;
|
||||
isInitializationStarted = true;
|
||||
|
||||
if (m_oViewModel != null)
|
||||
{
|
||||
// No need to create view model, set binding context an items source if already done.
|
||||
// If done twice tap events are fired multiple times (when hiding page using home button).
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -79,8 +81,8 @@ namespace TINK.View.MyBikes
|
|||
{
|
||||
Log.ForContext<MyBikesPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
|
||||
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
|
||||
isInitializationStarted = false;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
|
@ -89,6 +91,7 @@ namespace TINK.View.MyBikes
|
|||
MyBikesListView.ItemsSource = m_oViewModel;
|
||||
|
||||
await m_oViewModel.OnAppearing();
|
||||
isInitializationStarted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace TINK.Model.Bike.BC
|
|||
protected BikeInfo(
|
||||
IStateInfo stateInfo,
|
||||
string id,
|
||||
LockModel lockModel,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
|
@ -31,7 +32,7 @@ namespace TINK.Model.Bike.BC
|
|||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null)
|
||||
{
|
||||
Bike = new Bike(id, wheelType, typeOfBike, description);
|
||||
Bike = new Bike(id, lockModel, wheelType, typeOfBike, description);
|
||||
|
||||
m_oStateInfo = stateInfo;
|
||||
|
||||
|
@ -45,6 +46,7 @@ namespace TINK.Model.Bike.BC
|
|||
public BikeInfo(BikeInfo bikeInfo) : this(
|
||||
bikeInfo?.State,
|
||||
bikeInfo?.Id ?? throw new ArgumentException($"Can not copy-construct {typeof(BikeInfo).Name}-object. Source must not be null."),
|
||||
bikeInfo.LockModel,
|
||||
bikeInfo.IsDemo,
|
||||
bikeInfo.Group,
|
||||
bikeInfo.WheelType,
|
||||
|
@ -64,6 +66,7 @@ namespace TINK.Model.Bike.BC
|
|||
/// <param name="wheelType"></param>
|
||||
public BikeInfo(
|
||||
string id,
|
||||
LockModel lockModel,
|
||||
string stationId,
|
||||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null,
|
||||
|
@ -73,7 +76,8 @@ namespace TINK.Model.Bike.BC
|
|||
TypeOfBike? typeOfBike = null,
|
||||
string description = null) : this(
|
||||
new StateInfo(),
|
||||
id,
|
||||
id,
|
||||
lockModel,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
|
@ -88,7 +92,6 @@ namespace TINK.Model.Bike.BC
|
|||
/// <summary>
|
||||
/// Constructs a bike info object for a requested bike.
|
||||
/// </summary>
|
||||
/// <param name="dateTimeProvider">Provider for current date time to calculate remainig time on demand for state of type reserved.</param>
|
||||
/// <param name="wheelType"></param>
|
||||
/// <param name="id">Unique id of bike.</param>
|
||||
/// <param name="stationId">Name of station where bike is located, null if bike is on the road.</param>
|
||||
|
@ -97,9 +100,10 @@ namespace TINK.Model.Bike.BC
|
|||
/// <param name="requestedAt">Date time when bike was requested</param>
|
||||
/// <param name="mailAddress">Mail address of user which requested bike.</param>
|
||||
/// <param name="code">Booking code.</param>
|
||||
/// <param name="p_oDateTimeNowProvider">Date time provider to calculate reaining time.</param>
|
||||
/// <param name="dateTimeProvider">Date time provider to calculate reaining time.</param>
|
||||
public BikeInfo(
|
||||
string id,
|
||||
LockModel lockModel,
|
||||
bool? isDemo,
|
||||
IEnumerable<string> group,
|
||||
WheelType? wheelType,
|
||||
|
@ -117,7 +121,8 @@ namespace TINK.Model.Bike.BC
|
|||
requestedAt,
|
||||
mailAddress,
|
||||
code),
|
||||
id,
|
||||
id,
|
||||
lockModel,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
|
@ -134,6 +139,7 @@ namespace TINK.Model.Bike.BC
|
|||
/// </summary>
|
||||
/// <param name="dateTimeProvider">Provider for current date time to calculate remainig time on demand for state of type reserved.</param>
|
||||
/// <param name="wheelType"></param>
|
||||
/// <param name="lockModel">Specifies the lock model.</param>
|
||||
/// <param name="id">Unique id of bike.</param>
|
||||
/// <param name="currentStationId">Name of station where bike is located, null if bike is on the road.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
|
@ -143,6 +149,7 @@ namespace TINK.Model.Bike.BC
|
|||
/// <param name="code">Booking code.</param>
|
||||
public BikeInfo(
|
||||
string id,
|
||||
LockModel lockModel,
|
||||
bool? isDemo,
|
||||
IEnumerable<string> group,
|
||||
WheelType? wheelType,
|
||||
|
@ -158,7 +165,8 @@ namespace TINK.Model.Bike.BC
|
|||
bookedAt,
|
||||
mailAddress,
|
||||
code),
|
||||
id,
|
||||
id,
|
||||
lockModel,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
|
@ -198,6 +206,9 @@ namespace TINK.Model.Bike.BC
|
|||
|
||||
public TypeOfBike? TypeOfBike => Bike.TypeOfBike;
|
||||
|
||||
/// <summary> Gets the model of the lock. </summary>
|
||||
public LockModel LockModel => Bike.LockModel;
|
||||
|
||||
public string Description => Bike.Description;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace TINK.Model.Bike.BC
|
|||
/// <param name="stateInfo">Bike state info.</param>
|
||||
protected BikeInfoMutable(
|
||||
string id,
|
||||
LockModel lockModel,
|
||||
bool isDemo = BikeInfo.DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
|
@ -44,7 +45,7 @@ namespace TINK.Model.Bike.BC
|
|||
{
|
||||
IsDemo = isDemo;
|
||||
Group = group;
|
||||
m_oBike = new Bike(id, wheelType, typeOfBike, description);
|
||||
m_oBike = new Bike(id, lockModel, wheelType, typeOfBike, description);
|
||||
m_oStateInfo = new StateInfoMutable(dateTimeProvider, stateInfo);
|
||||
m_oStateInfo.PropertyChanged += (sender, eventargs) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(eventargs.PropertyName));
|
||||
StationId = stationId;
|
||||
|
@ -56,6 +57,7 @@ namespace TINK.Model.Bike.BC
|
|||
/// <summary> Constructs a bike object from source. </summary>
|
||||
public BikeInfoMutable(IBikeInfo bike, string stationName) : this(
|
||||
bike.Id,
|
||||
bike.LockModel,
|
||||
bike.IsDemo,
|
||||
bike.Group,
|
||||
bike.WheelType,
|
||||
|
@ -110,6 +112,8 @@ namespace TINK.Model.Bike.BC
|
|||
|
||||
public TypeOfBike? TypeOfBike => m_oBike.TypeOfBike;
|
||||
|
||||
public LockModel LockModel => m_oBike.LockModel;
|
||||
|
||||
public string Description => m_oBike.Description;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -31,6 +31,9 @@ namespace TINK.Model.Bike.BC
|
|||
/// </summary>
|
||||
TypeOfBike? TypeOfBike { get; }
|
||||
|
||||
/// <summary> Gets the model of the lock. </summary>
|
||||
LockModel LockModel { get; }
|
||||
|
||||
/// <summary> Holds the description of the bike. </summary>
|
||||
string Description { get; }
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ namespace TINK.Model.Bikes.Bike.BC
|
|||
/// </summary>
|
||||
TypeOfBike? TypeOfBike { get; }
|
||||
|
||||
/// <summary> Gets the model of the lock. </summary>
|
||||
LockModel LockModel { get; }
|
||||
|
||||
/// <summary> Holds the description of the bike. </summary>
|
||||
string Description { get; }
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.Model.Bike
|
||||
{
|
||||
{
|
||||
/// <summary> Count of wheels. </summary>
|
||||
public enum WheelType
|
||||
{
|
||||
|
@ -19,23 +19,36 @@ namespace TINK.Model.Bike
|
|||
Citybike = 2,
|
||||
}
|
||||
|
||||
/// <summary> Holds the model of lock. </summary>
|
||||
public enum LockModel
|
||||
{
|
||||
ILockIt, // haveltec GbmH Brandenburg, Germany bluetooth lock
|
||||
BordComputer, // Teilrad BC
|
||||
Sigo, // Sigo Gmbh Darmstadt, Germany bike lock
|
||||
}
|
||||
|
||||
/// <summary> Holds the type of lock. </summary>
|
||||
public enum LockType
|
||||
{
|
||||
Backend, // Backend, i.e. COPRI controls lock (open, close, ...)
|
||||
Bluethooth, // Lock is controlled.
|
||||
}
|
||||
|
||||
public class Bike : IEquatable<Bike>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a bike.
|
||||
/// </summary>
|
||||
/// <param name="dateTimeProvider">Provider for current date time to calculate remainig time on demand for state of type reserved.</param>
|
||||
/// <param name="wheelType"></param>
|
||||
/// <param name="p_iId">Unique id of bike.</param>
|
||||
/// <param name="p_strCurrentStationName">Name of station where bike is located, null if bike is on the road.</param>
|
||||
public Bike(
|
||||
string p_iId,
|
||||
LockModel lockModel,
|
||||
WheelType? wheelType = null,
|
||||
TypeOfBike? typeOfBike = null,
|
||||
string description = null)
|
||||
{
|
||||
WheelType = wheelType;
|
||||
TypeOfBike = typeOfBike;
|
||||
LockModel = lockModel;
|
||||
Id = p_iId;
|
||||
Description = description;
|
||||
}
|
||||
|
@ -55,6 +68,9 @@ namespace TINK.Model.Bike
|
|||
/// </summary>
|
||||
public TypeOfBike? TypeOfBike { get; }
|
||||
|
||||
/// <summary> Gets the model of the lock. </summary>
|
||||
public LockModel LockModel { get; private set; }
|
||||
|
||||
/// <summary> Holds the description of the bike. </summary>
|
||||
public string Description { get; }
|
||||
|
||||
|
|
23
TINKLib/Model/Bikes/Bike/BikeExtension.cs
Normal file
23
TINKLib/Model/Bikes/Bike/BikeExtension.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using TINK.Model.Bike;
|
||||
|
||||
namespace TINK.Model.Bikes.Bike
|
||||
{
|
||||
public static class BikeExtension
|
||||
{
|
||||
public static LockType GetLockType(this LockModel model)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case LockModel.ILockIt:
|
||||
return LockType.Bluethooth;
|
||||
|
||||
case LockModel.Sigo:
|
||||
return LockType.Backend;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported lock model {model} detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ namespace TINK.Model.Bike.BluetoothLock
|
|||
string description = null) : base(
|
||||
new StateInfo(),
|
||||
bikeId,
|
||||
LockModel.ILockIt,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
|
@ -81,6 +82,7 @@ namespace TINK.Model.Bike.BluetoothLock
|
|||
mailAddress,
|
||||
""),
|
||||
id,
|
||||
LockModel.ILockIt,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
|
@ -127,6 +129,7 @@ namespace TINK.Model.Bike.BluetoothLock
|
|||
mailAddress,
|
||||
""),
|
||||
id,
|
||||
LockModel.ILockIt,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
|
|
|
@ -7,7 +7,8 @@ namespace TINK.Model.Bike.BluetoothLock
|
|||
{
|
||||
/// <summary> Constructs a bike object from source. </summary>
|
||||
public BikeInfoMutable(BikeInfo bike, string stationName) : base(
|
||||
bike.Id,
|
||||
bike.Id,
|
||||
bike.LockModel,
|
||||
bike.IsDemo,
|
||||
bike.Group,
|
||||
bike.WheelType,
|
||||
|
|
141
TINKLib/Model/Bikes/Bike/CopriLock/BikeInfo.cs
Normal file
141
TINKLib/Model/Bikes/Bike/CopriLock/BikeInfo.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.Bikes.Bike;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Model.State;
|
||||
|
||||
namespace TINK.Model.Bike.CopriLock
|
||||
{
|
||||
public class BikeInfo : BC.BikeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a bike info object for a available bike.
|
||||
/// </summary>
|
||||
/// <param name="bikeId">Unique id of bike.</param>
|
||||
/// <param name="currentStationId">Id of station where bike is located.</param>
|
||||
/// <param name="lockInfo">Lock info.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <param name="tariffDescription">Hold tariff description of bike.</param>
|
||||
/// <param name="wheelType">Trike, two wheels, mono, ....</param>
|
||||
public BikeInfo(
|
||||
string bikeId,
|
||||
string currentStationId,
|
||||
LockInfo lockInfo,
|
||||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
TypeOfBike? typeOfBike = null,
|
||||
string description = null) : base(
|
||||
new StateInfo(),
|
||||
bikeId,
|
||||
LockModel.Sigo,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
typeOfBike,
|
||||
description,
|
||||
currentStationId,
|
||||
operatorUri,
|
||||
tariffDescription)
|
||||
{
|
||||
LockInfo = lockInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bike info object for a requested bike.
|
||||
/// </summary>
|
||||
/// <param name="id">Unique id of bike.</param>
|
||||
/// <param name="requestedAt">Date time when bike was requested</param>
|
||||
/// <param name="mailAddress">Mail address of user which requested bike.</param>
|
||||
/// <param name="currentStationId">Name of station where bike is located, null if bike is on the road.</param>
|
||||
/// <param name="lockInfo">Lock info.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <param name="tariffDescription">Hold tariff description of bike.</param>
|
||||
/// <param name="dateTimeProvider">Provider for current date time to calculate remainig time on demand for state of type reserved.</param>
|
||||
/// <param name="wheelType"></param>
|
||||
public BikeInfo(
|
||||
string id,
|
||||
DateTime requestedAt,
|
||||
string mailAddress,
|
||||
string currentStationId,
|
||||
LockInfo lockInfo,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription,
|
||||
Func<DateTime> dateTimeProvider,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
TypeOfBike? typeOfBike = null,
|
||||
string description = null) : base(
|
||||
new StateInfo(
|
||||
dateTimeProvider,
|
||||
requestedAt,
|
||||
mailAddress,
|
||||
""),
|
||||
id,
|
||||
LockModel.ILockIt,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
typeOfBike,
|
||||
description,
|
||||
currentStationId,
|
||||
operatorUri,
|
||||
tariffDescription)
|
||||
{
|
||||
LockInfo = lockInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bike info object for a booked bike.
|
||||
/// </summary>
|
||||
/// <param name="bookedAt">Date time when bike was booked</param>
|
||||
/// <param name="mailAddress">Mail address of user which booked bike.</param>
|
||||
/// <param name="currentStationId">Name of station where bike is located, null if bike is on the road.</param>
|
||||
/// <param name="lockInfo">Lock info.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <param name="tariffDescription">Hold tariff description of bike.</param>
|
||||
/// <param name="wheelType"></param>
|
||||
public BikeInfo(
|
||||
string id,
|
||||
DateTime bookedAt,
|
||||
string mailAddress,
|
||||
string currentStationId,
|
||||
LockInfo lockInfo,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription = null,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
TypeOfBike? typeOfBike = null,
|
||||
string description = null) : base(
|
||||
new StateInfo(
|
||||
bookedAt,
|
||||
mailAddress,
|
||||
""),
|
||||
id,
|
||||
LockModel.ILockIt,
|
||||
isDemo,
|
||||
group,
|
||||
wheelType,
|
||||
typeOfBike,
|
||||
description,
|
||||
currentStationId,
|
||||
operatorUri,
|
||||
tariffDescription)
|
||||
{
|
||||
LockInfo = lockInfo;
|
||||
}
|
||||
|
||||
public BikeInfo(BC.BikeInfo bikeInfo, LockInfo lockInfo) : base(
|
||||
bikeInfo ?? throw new ArgumentException($"Can not copy-construct {typeof(BikeInfo).Name}-object. Source bike info must not be null."))
|
||||
{
|
||||
LockInfo = lockInfo
|
||||
?? throw new ArgumentException($"Can not copy-construct {typeof(BikeInfo).Name}-object. Source lock object must not be null.");
|
||||
}
|
||||
|
||||
public LockInfo LockInfo { get; private set; }
|
||||
}
|
||||
}
|
31
TINKLib/Model/Bikes/Bike/CopriLock/BikeInfoMutable.cs
Normal file
31
TINKLib/Model/Bikes/Bike/CopriLock/BikeInfoMutable.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
|
||||
namespace TINK.Model.Bike.CopriLock
|
||||
{
|
||||
public class BikeInfoMutable : BC.BikeInfoMutable, IBikeInfoMutable
|
||||
{
|
||||
/// <summary> Constructs a bike object from source. </summary>
|
||||
public BikeInfoMutable(BikeInfo bike, string stationName) : base(
|
||||
bike?.Id ?? throw new ArgumentException(nameof(bike)),
|
||||
bike.LockModel,
|
||||
bike.IsDemo,
|
||||
bike.Group,
|
||||
bike.WheelType,
|
||||
bike.TypeOfBike,
|
||||
bike.Description,
|
||||
bike.StationId,
|
||||
stationName,
|
||||
bike.OperatorUri,
|
||||
bike.TariffDescription,
|
||||
() => DateTime.Now,
|
||||
bike.State)
|
||||
{
|
||||
LockInfo = new LockInfoMutable(bike.LockInfo.State);
|
||||
}
|
||||
|
||||
public LockInfoMutable LockInfo { get; }
|
||||
|
||||
ILockInfoMutable IBikeInfoMutable.LockInfo => LockInfo;
|
||||
}
|
||||
}
|
7
TINKLib/Model/Bikes/Bike/CopriLock/IBikeInfoMutable.cs
Normal file
7
TINKLib/Model/Bikes/Bike/CopriLock/IBikeInfoMutable.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace TINK.Model.Bikes.Bike.CopriLock
|
||||
{
|
||||
public interface IBikeInfoMutable : BC.IBikeInfoMutable
|
||||
{
|
||||
ILockInfoMutable LockInfo { get; }
|
||||
}
|
||||
}
|
8
TINKLib/Model/Bikes/Bike/CopriLock/ILockInfoMutable.cs
Normal file
8
TINKLib/Model/Bikes/Bike/CopriLock/ILockInfoMutable.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
namespace TINK.Model.Bikes.Bike.CopriLock
|
||||
{
|
||||
public interface ILockInfoMutable
|
||||
{
|
||||
LockingState State { get; set; }
|
||||
}
|
||||
}
|
33
TINKLib/Model/Bikes/Bike/CopriLock/LockInfoMutable.cs
Normal file
33
TINKLib/Model/Bikes/Bike/CopriLock/LockInfoMutable.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
|
||||
namespace TINK.Model.Bike.CopriLock
|
||||
{
|
||||
public class LockInfoMutable : ILockInfoMutable
|
||||
{
|
||||
/// <summary> Lock info object. </summary>
|
||||
private LockInfo LockInfo { get; set; }
|
||||
|
||||
/// <summary> Constructs a bluetooth lock info object. </summary>
|
||||
/// <param name="id">Id of lock must always been known when constructing an lock info object.</param>
|
||||
public LockInfoMutable(LockingState state)
|
||||
{
|
||||
LockInfo = new LockInfo.Builder() { State = state }.Build();
|
||||
}
|
||||
|
||||
public LockingState State
|
||||
{
|
||||
get => LockInfo.State;
|
||||
set => LockInfo = new LockInfo.Builder(LockInfo) { State = value }.Build();
|
||||
}
|
||||
|
||||
/// <summary> Holds the percentage of lock battery.</summary>
|
||||
public double BatteryPercentage { get; set; } = double.NaN;
|
||||
|
||||
/// <summary> Loads lock info object from values. </summary>
|
||||
public void Load()
|
||||
{
|
||||
LockInfo = new LockInfo.Builder(LockInfo) { }.Build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,8 +43,13 @@ namespace TINK.Model.Bike
|
|||
// Check if bike has to be added to list of existing station.
|
||||
if (ContainsKey(bikeInfo.Id) == false)
|
||||
{
|
||||
// Bike does not yet exist in list of bikes.
|
||||
Add(BikeInfoMutableFactory.Create(bikeInfo, stationName));
|
||||
var bikeInfoMutable = BikeInfoMutableFactory.Create(bikeInfo, stationName);
|
||||
if (bikeInfoMutable != null)
|
||||
{
|
||||
// Bike does not yet exist in list of bikes.
|
||||
Add(bikeInfoMutable);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -139,13 +144,24 @@ namespace TINK.Model.Bike
|
|||
/// <summary>
|
||||
/// Create mutable objects from immutable objects.
|
||||
/// </summary>
|
||||
private static class BikeInfoMutableFactory
|
||||
public static class BikeInfoMutableFactory
|
||||
{
|
||||
public static BikeInfoMutable Create(BikeInfo bikeInfo, string stationName)
|
||||
public static BikeInfoMutable Create(
|
||||
BikeInfo bikeInfo,
|
||||
string stationName)
|
||||
{
|
||||
return (bikeInfo is BluetoothLock.BikeInfo bluetoothLockBikeInfo)
|
||||
? new BluetoothLock.BikeInfoMutable(bluetoothLockBikeInfo, stationName)
|
||||
: new BikeInfoMutable(bikeInfo, stationName);
|
||||
if (bikeInfo is BluetoothLock.BikeInfo btBikeInfo)
|
||||
{
|
||||
return new BluetoothLock.BikeInfoMutable(btBikeInfo, stationName);
|
||||
}
|
||||
else if (bikeInfo is CopriLock.BikeInfo copriBikeInfo)
|
||||
{
|
||||
return new CopriLock.BikeInfoMutable(copriBikeInfo, stationName);
|
||||
}
|
||||
|
||||
// Unsupported type detected.
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ using TINK.Repository.Response;
|
|||
using TINK.Model.User.Account;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.MiniSurvey;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -111,7 +110,7 @@ namespace TINK.Model.Connector
|
|||
/// <param name="bike">Bike to update locking state for.</param>
|
||||
/// <param name="location">Location where lock was opened/ changed.</param>
|
||||
/// <returns>Response on updating locking state.</returns>
|
||||
public async Task StartReturningBike(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
public async Task StartReturningBike(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected request to notify about start of returning bike. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
|
@ -127,13 +126,20 @@ namespace TINK.Model.Connector
|
|||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task DoBook(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
public async Task DoBook(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected booking request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task BookAndOpenAync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected request to book and open bike detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<BookingFinishedModel> DoReturn(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
|
||||
Bikes.Bike.BC.IBikeInfoMutable bike,
|
||||
LocationDto location,
|
||||
ISmartDevice smartDevice)
|
||||
{
|
||||
|
@ -141,6 +147,14 @@ namespace TINK.Model.Connector
|
|||
return await Task.FromResult(new BookingFinishedModel());
|
||||
}
|
||||
|
||||
public async Task<BookingFinishedModel> ReturnAndCloseAsync(
|
||||
Bikes.Bike.CopriLock.IBikeInfoMutable bike,
|
||||
ISmartDevice smartDevice)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected close lock and return request detected. No user logged in.");
|
||||
return await Task.FromResult(new BookingFinishedModel());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits feedback to copri server.
|
||||
/// </summary>
|
||||
|
@ -162,5 +176,11 @@ namespace TINK.Model.Connector
|
|||
Log.ForContext<Command>().Error("Unexpected submit mini survey request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OpenLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public Task CloseLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ using TINK.Repository.Response;
|
|||
using TINK.Model.User.Account;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using TINK.Services.CopriApi;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ namespace TINK.Model.Connector
|
|||
throw;
|
||||
}
|
||||
|
||||
bike.Load(response, Mail, DateTimeProvider, Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
bike.Load(response, Mail, Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary> Request to cancel a reservation.</summary>
|
||||
|
@ -155,7 +155,6 @@ namespace TINK.Model.Connector
|
|||
bike,
|
||||
response,
|
||||
Mail,
|
||||
DateTimeProvider,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
|
@ -163,7 +162,7 @@ namespace TINK.Model.Connector
|
|||
/// <remarks> Operator specific call.</remarks>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <returns>Response on notification about start of returning sequence.</returns>
|
||||
public async Task StartReturningBike(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
public async Task StartReturningBike(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
|
@ -223,10 +222,10 @@ namespace TINK.Model.Connector
|
|||
{
|
||||
(await CopriServer.UpdateLockingStateAsync(
|
||||
bike.Id,
|
||||
state.Value,
|
||||
bike.OperatorUri,
|
||||
location,
|
||||
state.Value,
|
||||
bike.LockInfo.BatteryPercentage,
|
||||
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
bike.LockInfo.BatteryPercentage)).GetIsBookingResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -235,11 +234,9 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Request to book a bike. </summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task DoBook(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
public async Task DoBook(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
|
@ -250,56 +247,53 @@ namespace TINK.Model.Connector
|
|||
var btBike = bike as BikeInfoMutable;
|
||||
Guid guid = btBike != null ? btBike.LockInfo.Guid : new Guid();
|
||||
double batteryPercentage = btBike != null ? btBike.LockInfo.BatteryPercentage : double.NaN;
|
||||
try
|
||||
{
|
||||
response = (await CopriServer.DoBookAsync(
|
||||
bike.Id,
|
||||
guid,
|
||||
batteryPercentage,
|
||||
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent excepitons detected.
|
||||
throw;
|
||||
}
|
||||
|
||||
response = (await CopriServer.DoBookAsync(
|
||||
bike.Id,
|
||||
guid,
|
||||
batteryPercentage,
|
||||
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
|
||||
bike.Load(
|
||||
response,
|
||||
Mail,
|
||||
DateTimeProvider,
|
||||
Mail,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Books a bike and opens the lock.
|
||||
/// </summary>
|
||||
/// <param name="bike">Bike to book and open.</param>
|
||||
public async Task BookAndOpenAync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
=> await Polling.BookAndOpenAync(CopriServer, bike, Mail);
|
||||
|
||||
/// <summary> Request to return a bike.</summary>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <param name="locaton">Position of the bike.</param>
|
||||
/// <param name="locaton">Position of the bike for bluetooth locks.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
public async Task<BookingFinishedModel> DoReturn(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
|
||||
LocationDto location,
|
||||
ISmartDevice smartDevice)
|
||||
Bikes.Bike.BC.IBikeInfoMutable bike,
|
||||
LocationDto location = null,
|
||||
ISmartDevice smartDevice = null)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException("Can not return bike. No bike object available.");
|
||||
}
|
||||
|
||||
DoReturnResponse response;
|
||||
try
|
||||
{
|
||||
response = (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent exceptions detected.
|
||||
throw;
|
||||
}
|
||||
DoReturnResponse response
|
||||
= (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
|
||||
|
||||
bike.Load(Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
return response?.Create() ?? new BookingFinishedModel();
|
||||
}
|
||||
|
||||
/// <summary> Request to return bike and close the lock.</summary>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
public async Task<BookingFinishedModel> ReturnAndCloseAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike, ISmartDevice smartDevice = null)
|
||||
=> await Polling.ReturnAndCloseAync(CopriServer, smartDevice, bike);
|
||||
|
||||
/// <summary>
|
||||
/// Submits feedback to copri server.
|
||||
/// </summary>
|
||||
|
@ -316,5 +310,10 @@ namespace TINK.Model.Connector
|
|||
/// <param name="answers">Collection of answers.</param>
|
||||
public async Task DoSubmitMiniSurvey(IDictionary<string, string> answers)
|
||||
=> await CopriServer.DoSubmitMiniSurvey(answers);
|
||||
|
||||
public async Task OpenLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
=> await CopriServer.OpenAync(bike);
|
||||
public async Task CloseLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
=> await CopriServer.CloseAync(bike);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ using TINK.Repository.Request;
|
|||
using TINK.Model.User.Account;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.MiniSurvey;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -40,7 +39,7 @@ namespace TINK.Model.Connector
|
|||
/// <remarks> Operator specific call.</remarks>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <returns>Response on notification about start of returning sequence.</returns>
|
||||
Task StartReturningBike(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike);
|
||||
Task StartReturningBike(Bikes.Bike.BC.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Updates COPRI lock state for a booked bike. </summary>
|
||||
/// <param name="bike">Bike to update locking state for.</param>
|
||||
|
@ -50,13 +49,30 @@ namespace TINK.Model.Connector
|
|||
|
||||
/// <summary> Request to book a bike.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
Task DoBook(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike);
|
||||
Task DoBook(Bikes.Bike.BC.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to book a bike and open its lock.</summary>
|
||||
/// <param name="bike">Bike to book and to open lock for.</param>
|
||||
Task BookAndOpenAync(Bikes.Bike.CopriLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to open lock.</summary>
|
||||
/// <param name="bike">Bike for which lock has to be opened.</param>
|
||||
Task OpenLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to close lock.</summary>
|
||||
/// <param name="bike">Bike for which lock has to be closed.</param>
|
||||
Task CloseLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to return a bike.</summary>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <param name="location">Geolocation of lock when returning bike.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
Task<BookingFinishedModel> DoReturn(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto geolocation = null, ISmartDevice smartDevice = null);
|
||||
Task<BookingFinishedModel> DoReturn(Bikes.Bike.BC.IBikeInfoMutable bike, LocationDto geolocation = null, ISmartDevice smartDevice = null);
|
||||
|
||||
/// <summary> Request to return bike and close the lock.</summary>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
Task<BookingFinishedModel> ReturnAndCloseAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike, ISmartDevice smartDevice = null);
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
bool IsConnected { get; }
|
||||
|
|
|
@ -182,6 +182,29 @@ namespace TINK.Model.Connector
|
|||
&& bikeInfo.system.ToUpper().StartsWith("ILOCKIT");
|
||||
}
|
||||
|
||||
/// <summary> Gets whether the bike Is a Sigo bike or not. </summary>
|
||||
/// <param name="bikeInfo">JSON to get information from..</param>
|
||||
/// <returns>True if bike is a Sigo.</returns>
|
||||
public static bool GetIsSigoBike(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
return !string.IsNullOrEmpty(bikeInfo.system)
|
||||
&& bikeInfo.system.ToUpper().StartsWith("SIGO");
|
||||
}
|
||||
|
||||
public static LockModel? GetLockModel(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
if (GetIsBluetoothLockBike(bikeInfo))
|
||||
return LockModel.ILockIt;
|
||||
|
||||
if (GetIsManualLockBike(bikeInfo))
|
||||
return LockModel.BordComputer;
|
||||
|
||||
if (GetIsSigoBike(bikeInfo))
|
||||
return LockModel.Sigo;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary> Gets whether the bike has a bord computer or not. </summary>
|
||||
/// <param name="bikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
|
@ -334,6 +357,36 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the locking state from response.
|
||||
/// </summary>
|
||||
/// <param name="bikeInfo"> Response locking state from.</param>
|
||||
/// <returns>Locking state</returns>
|
||||
public static Bikes.Bike.CopriLock.LockingState GetCopriLockingState(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(bikeInfo?.lock_state))
|
||||
return Bikes.Bike.CopriLock.LockingState.UnknownDisconnected;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "locked".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Closed;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "locking".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Closing;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "unlocked".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Open;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "unlocking".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Opening;
|
||||
|
||||
return Bikes.Bike.CopriLock.LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operator Uri from response.
|
||||
/// </summary>
|
||||
/// <param name="bikeInfo"> Response to get uri from.</param>
|
||||
/// <returns>Operatore Uri</returns>
|
||||
public static Uri GetOperatorUri(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
return bikeInfo?.uri_operator != null && !string.IsNullOrEmpty(bikeInfo?.uri_operator)
|
||||
|
|
|
@ -10,6 +10,8 @@ using Serilog;
|
|||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.Bike.BC.IBikeInfoMutable;
|
||||
using BikeExtension = TINK.Model.Bikes.Bike.BikeExtension;
|
||||
|
||||
using System.Globalization;
|
||||
using TINK.Model.Station.Operator;
|
||||
using Xamarin.Forms;
|
||||
|
@ -71,7 +73,7 @@ namespace TINK.Model.Connector
|
|||
station.Value.operator_data?.operator_phone,
|
||||
station.Value.operator_data?.operator_hours,
|
||||
station.Value.operator_data?.operator_email,
|
||||
!string.IsNullOrEmpty(station.Value.operator_data?.operator_color)
|
||||
!string.IsNullOrEmpty(station.Value.operator_data?.operator_color)
|
||||
? Color.FromHex(station.Value.operator_data?.operator_color)
|
||||
: (Color?)null)));
|
||||
}
|
||||
|
@ -86,10 +88,10 @@ namespace TINK.Model.Connector
|
|||
/// <returns>General data object initialized form COPRI response.</returns>
|
||||
public static GeneralData GetGeneralData(this ResponseBase response)
|
||||
=> new GeneralData(
|
||||
response.init_map.GetMapSpan(),
|
||||
response.merchant_message,
|
||||
response.TryGetCopriVersion(out Version copriVersion)
|
||||
? new Version(0,0)
|
||||
response.init_map.GetMapSpan(),
|
||||
response.merchant_message,
|
||||
response.TryGetCopriVersion(out Version copriVersion)
|
||||
? new Version(0, 0)
|
||||
: copriVersion,
|
||||
new ResourceUrls(response.tariff_info_html, response.bike_info_html, response.agb_html, response.privacy_html, response.impress_html));
|
||||
|
||||
|
@ -116,28 +118,21 @@ namespace TINK.Model.Connector
|
|||
loginResponse.authcookie?.Replace(merchantId, ""),
|
||||
loginResponse.GetGroup(),
|
||||
loginResponse.debuglevel == 1
|
||||
? Permissions.All :
|
||||
(Permissions)loginResponse.debuglevel) ;
|
||||
? Permissions.All :
|
||||
(Permissions)loginResponse.debuglevel);
|
||||
}
|
||||
|
||||
/// <summary> Load bike object from booking response. </summary>
|
||||
/// <param name="bike">Bike object to load from response.</param>
|
||||
/// <param name="bikeInfo">Booking response.</param>
|
||||
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
||||
/// <param name="p_strSessionCookie">Session cookie of user which books bike.</param>
|
||||
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
||||
public static void Load(
|
||||
this IBikeInfoMutable bike,
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.Bike.BC.NotifyPropertyChangedLevel.All)
|
||||
{
|
||||
|
||||
var l_oDateTimeProvider = dateTimeProvider != null
|
||||
? dateTimeProvider
|
||||
: () => DateTime.Now;
|
||||
|
||||
if (bike is Bike.BluetoothLock.BikeInfoMutable btBikeInfo)
|
||||
{
|
||||
btBikeInfo.LockInfo.Load(
|
||||
|
@ -171,7 +166,7 @@ namespace TINK.Model.Connector
|
|||
InUseStateEnum.Booked,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
bikeInfo.timeCode,
|
||||
notifyLevel);
|
||||
break;
|
||||
|
||||
|
@ -293,15 +288,22 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs bike info instances/ bike info derived instances.
|
||||
/// </summary>
|
||||
public static class BikeInfoFactory
|
||||
{
|
||||
/// <summary> Set default lock type to . </summary>
|
||||
public static LockModel DEFAULTLOCKMODEL = LockModel.Sigo;
|
||||
|
||||
/// <summary> Creates a bike info object from copri response. </summary>
|
||||
/// <param name="bikeInfo">Copri response for a disposable bike. </param>
|
||||
public static BikeInfo Create(BikeInfoAvailable bikeInfo)
|
||||
{
|
||||
if (bikeInfo.GetIsManualLockBike())
|
||||
var lockModel = bikeInfo.GetLockModel();
|
||||
|
||||
if (lockModel.HasValue
|
||||
&& lockModel.Value == LockModel.BordComputer)
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
|
@ -309,6 +311,7 @@ namespace TINK.Model.Connector
|
|||
"Manual lock bikes are no more supported." +
|
||||
$"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $"station number {bikeInfo.station}" : string.Empty)}."
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -329,40 +332,52 @@ namespace TINK.Model.Connector
|
|||
return null;
|
||||
}
|
||||
|
||||
var lockType = lockModel.HasValue
|
||||
? BikeExtension.GetLockType(lockModel.Value)
|
||||
: BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to backend- locks.
|
||||
|
||||
try
|
||||
{
|
||||
return !bikeInfo.GetIsBluetoothLockBike()
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
switch (lockType)
|
||||
{
|
||||
case LockType.Backend:
|
||||
return new Bike.CopriLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.station,
|
||||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState()}.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription) null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
case LockType.Bluethooth:
|
||||
return new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported lock type {lockType} detected.");
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
|
@ -376,13 +391,15 @@ namespace TINK.Model.Connector
|
|||
/// <param name="bikeInfo">Copri response. </param>
|
||||
/// <param name="mailAddress">Mail address of user.</param>
|
||||
/// <param name="dateTimeProvider">Date and time provider function.</param>
|
||||
/// <returns></returns>
|
||||
public static BikeInfo Create(
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
if (bikeInfo.GetIsManualLockBike())
|
||||
var lockModel = bikeInfo.GetLockModel();
|
||||
|
||||
if (lockModel.HasValue
|
||||
&& lockModel.Value == LockModel.BordComputer)
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
|
@ -393,8 +410,11 @@ namespace TINK.Model.Connector
|
|||
return null;
|
||||
}
|
||||
|
||||
var lockType = lockModel.HasValue
|
||||
? BikeExtension.GetLockType(lockModel.Value)
|
||||
: BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to backend- locks.
|
||||
|
||||
// Check if bike is a bluetooth lock bike.
|
||||
var isBluetoothBike = bikeInfo.GetIsBluetoothLockBike();
|
||||
int lockSerial = bikeInfo.GetBluetoothLockId();
|
||||
Guid lockGuid = bikeInfo.GetBluetoothLockGuid();
|
||||
|
||||
|
@ -403,48 +423,54 @@ namespace TINK.Model.Connector
|
|||
case InUseStateEnum.Reserved:
|
||||
try
|
||||
{
|
||||
return !isBluetoothBike
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
switch (lockType)
|
||||
{
|
||||
case LockType.Bluethooth:
|
||||
return new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
lockGuid,
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
case LockType.Backend:
|
||||
return new Bike.CopriLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
dateTimeProvider)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
lockGuid,
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported lock type {lockType} detected.");
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
|
@ -456,45 +482,69 @@ namespace TINK.Model.Connector
|
|||
case InUseStateEnum.Booked:
|
||||
try
|
||||
{
|
||||
return !isBluetoothBike
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
switch (lockModel)
|
||||
{
|
||||
case LockModel.ILockIt:
|
||||
return new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
case LockModel.BordComputer:
|
||||
return new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
LockModel.BordComputer,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode);
|
||||
default:
|
||||
return new Bike.CopriLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
|
@ -517,7 +567,7 @@ namespace TINK.Model.Connector
|
|||
#if USCSHARP9
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null,
|
||||
#else
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?) null,
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?)null,
|
||||
#endif
|
||||
FreeTimePerSession = double.TryParse(tariffDesciption?.free_hours, NumberStyles.Any, CultureInfo.InvariantCulture, out double freeHours) ? TimeSpan.FromHours(freeHours) : TimeSpan.Zero,
|
||||
FeeEuroPerHour = double.TryParse(tariffDesciption?.eur_per_hour, NumberStyles.Any, CultureInfo.InvariantCulture, out double euroPerHour) ? euroPerHour : double.NaN,
|
||||
|
@ -545,11 +595,11 @@ namespace TINK.Model.Connector
|
|||
|
||||
var miniquery = response.user_miniquery;
|
||||
bookingFinished.MiniSurvey = new MiniSurveyModel
|
||||
{
|
||||
Title = miniquery.title,
|
||||
Subtitle = miniquery.subtitle,
|
||||
Footer = miniquery.footer
|
||||
};
|
||||
{
|
||||
Title = miniquery.title,
|
||||
Subtitle = miniquery.subtitle,
|
||||
Footer = miniquery.footer
|
||||
};
|
||||
|
||||
foreach (var question in miniquery?.questions?.OrderBy(x => x.Key) ?? new Dictionary<string, MiniSurveyResponse.Question>().OrderBy(x => x.Key))
|
||||
{
|
||||
|
|
|
@ -7,17 +7,16 @@ namespace TINK.Model.State
|
|||
InUseStateEnum Value { get; }
|
||||
|
||||
/// <summary> Updates state from webserver. </summary>
|
||||
/// <param name="p_oState">State of the bike.</param>
|
||||
/// <param name="p_oFrom">Date time when bike was reserved/ booked.</param>
|
||||
/// <param name="p_oDuration">Lenght of time span for which bike remains booked.</param>
|
||||
/// <param name="p_strMailAddress">Mailaddress of the one which reserved/ booked.</param>
|
||||
/// <param name="p_strCode">Booking code if bike is booked or reserved.</param>
|
||||
/// <param name="state">State of the bike.</param>
|
||||
/// <param name="from">Date time when bike was reserved/ booked.</param>
|
||||
/// <param name="mailAddress">Mailaddress of the one which reserved/ booked.</param>
|
||||
/// <param name="code">Booking code if bike is booked or reserved.</param>
|
||||
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
||||
void Load(
|
||||
InUseStateEnum p_oState,
|
||||
DateTime? p_oFrom = null,
|
||||
string p_strMailAddress = null,
|
||||
string p_strCode = null,
|
||||
InUseStateEnum state,
|
||||
DateTime? from = null,
|
||||
string mailAddress = null,
|
||||
string code = null,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.Bike.BC.NotifyPropertyChangedLevel.All);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace TINK.Model
|
|||
public void SetWhatsNewWasShown() => WhatsNew = WhatsNew.SetWasShown();
|
||||
|
||||
/// <summary>Holds uris of copri servers. </summary>
|
||||
public CopriServerUriList Uris { get; }
|
||||
public CopriServerUriList Uris { get; private set; }
|
||||
|
||||
/// <summary> Holds the filters loaded from settings. </summary>
|
||||
public IGroupFilterSettings FilterGroupSetting { get; set; }
|
||||
|
@ -298,7 +298,8 @@ namespace TINK.Model
|
|||
return;
|
||||
}
|
||||
|
||||
// Set active app theme
|
||||
// Set active app theme from settings.
|
||||
// Value might differ from default scheme value defined in ResourceDictionary.MergedDictionaries (App.xaml)
|
||||
ICollection<ResourceDictionary> mergedDictionaries = Application.Current.Resources.MergedDictionaries;
|
||||
if (mergedDictionaries == null)
|
||||
{
|
||||
|
@ -399,7 +400,7 @@ namespace TINK.Model
|
|||
public IServicesContainer<IGeolocation> GeolocationServices { get; }
|
||||
|
||||
/// <summary> Manages the different types of LocksService objects.</summary>
|
||||
public ServicesContainerMutable<object> Themes { get; }
|
||||
public ServicesContainerMutable<object> Themes { get; private set; }
|
||||
|
||||
/// <summary> Object to switch logging level. </summary>
|
||||
private LoggingLevelSwitch m_oLoggingLevelSwitch;
|
||||
|
|
|
@ -514,6 +514,10 @@ namespace TINK.Model
|
|||
{
|
||||
new Version(3, 0, 290),
|
||||
AppResources.ChangeLog3_0_290
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 294),
|
||||
AppResources.ChangeLog3_0_293
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1033,6 +1033,17 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Target Android framework set from 11.0 to 12.0 (Level 31 S).
|
||||
///NuGet packages updated.
|
||||
///Started adding support for new lock type..
|
||||
/// </summary>
|
||||
public static string ChangeLog3_0_293 {
|
||||
get {
|
||||
return ResourceManager.GetString("ChangeLog3_0_293", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Lock of rented bike cannot be be connected right now..
|
||||
/// </summary>
|
||||
|
@ -1356,6 +1367,24 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error returning bike!.
|
||||
/// </summary>
|
||||
public static string ErrorReturnBikeTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorReturnBikeTitle", 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 Error returning bike!.
|
||||
/// </summary>
|
||||
|
|
|
@ -869,4 +869,10 @@ Activity Indicator zu Konto-Verwaltungsseiten hinzugefügt und Logging erweitert
|
|||
<data name="ChangeLog3_0_290" xml:space="preserve">
|
||||
<value>Der aktuelle Standort wird mit höherer Genauigkeit abgefragt.</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_293" xml:space="preserve">
|
||||
<value>Target Android framework von 11.0 auf 12.0 (Level 31 S) gesetzt.
|
||||
NuGet packages aktualisiert.
|
||||
Entwurf Untertützung eines neuen Schlosstyps hinzugefügt.
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
|
@ -962,4 +962,9 @@ Activity indicator added account management pages and logging extended.</value>
|
|||
<data name="ChangeLog3_0_290" xml:space="preserve">
|
||||
<value>Geolocation is queried with higher accuracy.</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_293" xml:space="preserve">
|
||||
<value>Target Android framework set from 11.0 to 12.0 (Level 31 S).
|
||||
NuGet packages updated.
|
||||
Started adding support for new lock type.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1182,6 +1182,15 @@ Activity Indicator zu Konto-Verwaltungsseiten hinzugefügt und Logging erweitert
|
|||
<source>Geolocation is queried with higher accuracy.</source>
|
||||
<target state="translated">Der aktuelle Standort wird mit höherer Genauigkeit abgefragt.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ChangeLog3_0_293" translate="yes" xml:space="preserve">
|
||||
<source>Target Android framework set from 11.0 to 12.0 (Level 31 S).
|
||||
NuGet packages updated.
|
||||
Started adding support for new lock type.</source>
|
||||
<target state="translated">Target Android framework von 11.0 auf 12.0 (Level 31 S) gesetzt.
|
||||
NuGet packages aktualisiert.
|
||||
Entwurf Untertützung eines neuen Schlosstyps hinzugefügt.
|
||||
</target>
|
||||
</trans-unit>
|
||||
</group>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -180,13 +180,13 @@ namespace TINK.Repository
|
|||
/// <returns>Response on updating locking state.</returns>
|
||||
public async Task<ReservationBookingResponse> UpdateLockingStateAsync(
|
||||
string bikeId,
|
||||
LocationDto location,
|
||||
lock_state state,
|
||||
double batteryLevel,
|
||||
Uri operatorUri)=>
|
||||
Uri operatorUri,
|
||||
LocationDto location,
|
||||
double batteryLevel) =>
|
||||
await DoUpdateLockingStateAsync(
|
||||
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
|
||||
requestBuilder.UpateLockingState(bikeId, location, state, batteryLevel),
|
||||
requestBuilder.UpateLockingState(bikeId, state, location, batteryLevel),
|
||||
UserAgent);
|
||||
|
||||
/// <summary> Gets booking request request. </summary>
|
||||
|
@ -200,12 +200,22 @@ namespace TINK.Repository
|
|||
Guid guid,
|
||||
double batteryPercentage,
|
||||
Uri operatorUri)
|
||||
{
|
||||
return await DoBookAsync(
|
||||
=> await DoBookAsync(
|
||||
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
|
||||
requestBuilder.DoBook(bikeId, guid, batteryPercentage),
|
||||
UserAgent);
|
||||
}
|
||||
|
||||
/// <summary> Books a bike and starts opening bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on booking request.</returns>
|
||||
public async Task<ReservationBookingResponse> BookAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> await DoBookAsync(
|
||||
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
|
||||
requestBuilder.BookAndStartOpening(bikeId),
|
||||
UserAgent);
|
||||
|
||||
/// <summary> Returns a bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
|
@ -218,12 +228,24 @@ namespace TINK.Repository
|
|||
LocationDto location,
|
||||
ISmartDevice smartDevice,
|
||||
Uri operatorUri)
|
||||
{
|
||||
return await DoReturn(
|
||||
=> await DoReturn(
|
||||
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
|
||||
requestBuilder.DoReturn(bikeId, location, smartDevice),
|
||||
UserAgent);
|
||||
}
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on returning request.</returns>
|
||||
public async Task<DoReturnResponse> ReturnAndStartClosingAsync(
|
||||
string bikeId,
|
||||
ISmartDevice smartDevice,
|
||||
Uri operatorUri)
|
||||
=> await DoReturn(
|
||||
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
|
||||
requestBuilder.ReturnAndStartClosing(bikeId, smartDevice),
|
||||
UserAgent);
|
||||
|
||||
/// <summary> Submits feedback to copri server. </summary>
|
||||
/// <param name="bikeId">Id of the bike to which the feedback is related to.</param>
|
||||
|
@ -630,10 +652,10 @@ namespace TINK.Repository
|
|||
string agent = null)
|
||||
{
|
||||
#if !WINDOWS_UWP
|
||||
string l_oBikesAvaialbeResponse;
|
||||
string bikesAvaialbeResponse;
|
||||
try
|
||||
{
|
||||
l_oBikesAvaialbeResponse = await PostAsync(copriHost, command, agent);
|
||||
bikesAvaialbeResponse = await PostAsync(copriHost, command, agent);
|
||||
}
|
||||
catch (System.Exception exception)
|
||||
{
|
||||
|
@ -651,7 +673,7 @@ namespace TINK.Repository
|
|||
}
|
||||
|
||||
// Extract bikes from response.
|
||||
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
|
||||
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(bikesAvaialbeResponse)?.shareejson;
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
|
|
|
@ -1380,7 +1380,7 @@ namespace TINK.Repository
|
|||
get
|
||||
{
|
||||
var l_iCount = 1;
|
||||
while (GetBikesAvailable(CopriDevelHostUri, MerchantId, p_eSampleSet: ActiveSampleSet, p_lStageIndex: l_iCount) != null)
|
||||
while (GetBikesAvailable(CopriDevelHostUri, MerchantId, sampleSet: ActiveSampleSet, stageIndex: l_iCount) != null)
|
||||
{
|
||||
l_iCount++;
|
||||
}
|
||||
|
@ -1474,21 +1474,21 @@ namespace TINK.Repository
|
|||
/// <summary>
|
||||
/// Gets list of bikes from memory.
|
||||
/// </summary>
|
||||
/// <param name="p_strMerchantId">Id of the merchant.</param>
|
||||
/// <param name="p_strSessionCookie">Auto cookie of user if user is logged in.</param>
|
||||
/// <param name="p_eSampleSet">Set of samples.</param>
|
||||
/// <param name="p_lStageIndex">Index of the stage.</param>
|
||||
/// <param name="merchantId">Id of the merchant.</param>
|
||||
/// <param name="sessionCookie">Auto cookie of user if user is logged in.</param>
|
||||
/// <param name="sampleSet">Set of samples.</param>
|
||||
/// <param name="stageIndex">Index of the stage.</param>
|
||||
/// <returns></returns>
|
||||
public static BikesAvailableResponse GetBikesAvailable(
|
||||
string p_strMerchantId,
|
||||
string p_strSessionCookie = null,
|
||||
SampleSets p_eSampleSet = DEFAULT_SAMPLE_SET,
|
||||
long p_lStageIndex = DEFAULT_STAGE_INDEX)
|
||||
string merchantId,
|
||||
string sessionCookie = null,
|
||||
SampleSets sampleSet = DEFAULT_SAMPLE_SET,
|
||||
long stageIndex = DEFAULT_STAGE_INDEX)
|
||||
{
|
||||
switch (p_eSampleSet)
|
||||
switch (sampleSet)
|
||||
{
|
||||
case SampleSets.Set1:
|
||||
switch (p_lStageIndex)
|
||||
switch (stageIndex)
|
||||
{
|
||||
case 1:
|
||||
return CopriCallsStatic.DeserializeResponse<BikesAvailableResponse>(BIKES_AVAILABLE_SET01_001_FILE);
|
||||
|
@ -1497,7 +1497,7 @@ namespace TINK.Repository
|
|||
return null;
|
||||
}
|
||||
case SampleSets.Set2:
|
||||
switch (p_lStageIndex)
|
||||
switch (stageIndex)
|
||||
{
|
||||
case 1:
|
||||
return CopriCallsStatic.DeserializeResponse<BikesAvailableResponse>(BIKES_AVAILABLE_SET02_001_FILE);
|
||||
|
@ -1513,7 +1513,7 @@ namespace TINK.Repository
|
|||
}
|
||||
|
||||
case SampleSets.ShareeFr01_Set1:
|
||||
switch (p_lStageIndex)
|
||||
switch (stageIndex)
|
||||
{
|
||||
case 1:
|
||||
return CopriCallsStatic.DeserializeResponse<BikesAvailableResponse>(BIKES_AVAILABLE_REQUEST_SHAREEFR01_SET1_FILE);
|
||||
|
@ -1619,15 +1619,18 @@ namespace TINK.Repository
|
|||
|
||||
public Task<ReservationBookingResponse> UpdateLockingStateAsync(
|
||||
string bikeId,
|
||||
LocationDto geolocation,
|
||||
lock_state state,
|
||||
double batteryLevel,
|
||||
Uri operatorUri) => null;
|
||||
Uri operatorUri,
|
||||
LocationDto geolocation,
|
||||
double batteryLevel) => null;
|
||||
|
||||
public Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
=> null;
|
||||
|
||||
public Task<ReservationBookingResponse> BookAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> null;
|
||||
|
||||
public Task<DoReturnResponse> DoReturn(
|
||||
string bikeId,
|
||||
|
@ -1636,6 +1639,12 @@ namespace TINK.Repository
|
|||
Uri operatorUri)
|
||||
=> null;
|
||||
|
||||
public Task<DoReturnResponse> ReturnAndStartClosingAsync(
|
||||
string bikeId,
|
||||
ISmartDevice smartDevice,
|
||||
Uri operatorUri)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri operatorUri)
|
||||
=> null;
|
||||
|
||||
|
|
|
@ -166,16 +166,23 @@ namespace TINK.Repository
|
|||
|
||||
public Task<ReservationBookingResponse> UpdateLockingStateAsync(
|
||||
string bikeId,
|
||||
LocationDto geolocation,
|
||||
lock_state state,
|
||||
double batteryLevel,
|
||||
Uri operatorUri)
|
||||
Uri operatorUri,
|
||||
LocationDto geolocation,
|
||||
double batteryLevel)
|
||||
=> throw new System.Exception("Aktualisierung des Schlossstatuses im Offlinemodus nicht möglich!");
|
||||
|
||||
public Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
|
||||
{
|
||||
throw new System.Exception("Buchung im Offlinemodus nicht möglich!");
|
||||
}
|
||||
=> throw new System.Exception("Buchung im Offlinemodus nicht möglich!");
|
||||
|
||||
/// <summary> Books a bike and starts opening bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on booking request.</returns>
|
||||
public Task<ReservationBookingResponse> BookAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> throw new System.Exception("Buchung mit Start von Schlossöffnen ist im Offlinemodus nicht möglich!");
|
||||
|
||||
public Task<DoReturnResponse> DoReturn(
|
||||
string bikeId,
|
||||
|
@ -184,6 +191,17 @@ namespace TINK.Repository
|
|||
Uri operatorUri)
|
||||
=> throw new System.Exception("Rückgabe im Offlinemodus nicht möglich!");
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on returning request.</returns>
|
||||
public Task<DoReturnResponse> ReturnAndStartClosingAsync(
|
||||
string bikeId,
|
||||
ISmartDevice smartDevice,
|
||||
Uri operatorUri)
|
||||
=> throw new System.Exception("Rückgabe mit Schloss schließen Befehl im Offlinemodus nicht möglich!");
|
||||
|
||||
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri operatorUri) =>
|
||||
throw new System.Exception("Übermittlung von Feedback im Offlinemodus nicht möglich!");
|
||||
|
||||
|
|
|
@ -66,12 +66,12 @@ namespace TINK.Repository
|
|||
/// <returns>Response on updating locking state.</returns>
|
||||
Task<ReservationBookingResponse> UpdateLockingStateAsync(
|
||||
string bikeId,
|
||||
LocationDto location,
|
||||
lock_state state,
|
||||
double batteryPercentage,
|
||||
Uri operatorUri);
|
||||
Uri operatorUri,
|
||||
LocationDto location = null,
|
||||
double batteryPercentage = double.NaN);
|
||||
|
||||
/// <summary> Books a bike. </summary>
|
||||
/// <summary> Books a bluetooth bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <param name="guid">Used to publish GUID from app to copri. Used for initial setup of bike in copri.</param>
|
||||
/// <param name="batteryPercentage">Holds the filling level percentage of the battery.</param>
|
||||
|
@ -83,6 +83,14 @@ namespace TINK.Repository
|
|||
double batteryPercentage,
|
||||
Uri operatorUri);
|
||||
|
||||
/// <summary> Books a bike and starts opening bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on booking request.</returns>
|
||||
Task<ReservationBookingResponse> BookAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri);
|
||||
|
||||
/// <summary> Returns a bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="location">Geolocation of lock.</param>
|
||||
|
@ -95,6 +103,16 @@ namespace TINK.Repository
|
|||
ISmartDevice smartDevice,
|
||||
Uri operatorUri);
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on returning request.</returns>
|
||||
Task<DoReturnResponse> ReturnAndStartClosingAsync(
|
||||
string bikeId,
|
||||
ISmartDevice smartDevice,
|
||||
Uri operatorUri);
|
||||
|
||||
/// <summary>
|
||||
/// Submits feedback to copri server.
|
||||
/// </summary>
|
||||
|
|
|
@ -68,23 +68,34 @@ namespace TINK.Repository.Request
|
|||
/// <returns>Request to update locking state.</returns>
|
||||
string UpateLockingState(
|
||||
string bikeId,
|
||||
LocationDto location,
|
||||
lock_state state,
|
||||
double batteryPercentage);
|
||||
LocationDto location = null,
|
||||
double batteryPercentage = double.NaN);
|
||||
|
||||
/// <summary> Gets booking request request (synonym: booking == renting == mieten). </summary>
|
||||
/// <summary> Gets the booking request (synonym: booking == renting == mieten). </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <param name="guid">Used to publish GUID from app to copri. Used for initial setup of bike in copri.</param>
|
||||
/// <param name="batteryPercentage">Holds the filling level percentage of the battery.</param>
|
||||
/// <returns>Request to booking bike.</returns>
|
||||
string DoBook(string bikeId, Guid guid, double batteryPercentage);
|
||||
|
||||
/// <summary> Gets the request to book and start opening the bike (synonym: booking == renting == mieten). </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <returns>Request to booking bike.</returns>
|
||||
string BookAndStartOpening(string bikeId);
|
||||
|
||||
/// <summary> Gets request for returning the bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="location">Geolocation of lock when returning bike.</param>
|
||||
/// <returns>Requst on returning request.</returns>
|
||||
string DoReturn(string bikeId, LocationDto location, ISmartDevice smartDevice);
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
/// <returns>Response to send to corpi.</returns>
|
||||
string ReturnAndStartClosing(string bikeId, ISmartDevice smartDevice);
|
||||
|
||||
/// <summary>
|
||||
/// Gets request for submiting feedback to copri server.
|
||||
/// </summary>
|
||||
|
@ -103,8 +114,10 @@ namespace TINK.Repository.Request
|
|||
/// <summary> Copri locking states</summary>
|
||||
public enum lock_state
|
||||
{
|
||||
locking,
|
||||
locked,
|
||||
unlocked
|
||||
unlocking,
|
||||
unlocked,
|
||||
}
|
||||
|
||||
/// <summary> Holds lockation info.</summary>
|
||||
|
|
|
@ -103,13 +103,24 @@ namespace TINK.Repository.Request
|
|||
public string StartReturningBike(string bikeId)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
public string UpateLockingState(string bikeId, LocationDto geolocation, lock_state state, double batteryPercentage)
|
||||
public string UpateLockingState(string bikeId, lock_state state, LocationDto geolocation, double batteryPercentage)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
public string DoBook(string bikeId, Guid guid, double batteryPercentage) => throw new NotSupportedException();
|
||||
|
||||
/// <summary> Gets the request to book and start opening the bike (synonym: booking == renting == mieten). </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <returns>Request to booking bike.</returns>
|
||||
public string BookAndStartOpening(string bikeId) => throw new NotSupportedException();
|
||||
|
||||
public string DoReturn(string bikeId, LocationDto geolocation, ISmartDevice smartDevice) => throw new NotSupportedException();
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
/// <returns>Response to send to corpi.</returns>
|
||||
public string ReturnAndStartClosing(string bikeId, ISmartDevice smartDevice) => throw new NotSupportedException();
|
||||
|
||||
/// <summary> Gets submit feedback request. </summary>
|
||||
/// <param name="bikeId">Id of the bike to which the feedback is related to.</param>
|
||||
/// <param name="message">General purpose message or error description.</param>
|
||||
|
|
|
@ -107,7 +107,11 @@ namespace TINK.Repository.Request
|
|||
/// <param name="bikeId">Id of the bike to update locking state for.</param>
|
||||
/// <param name="state">New locking state.</param>
|
||||
/// <returns>Request to update locking state.</returns>
|
||||
public string UpateLockingState(string bikeId, LocationDto geolocation, lock_state state, double batteryPercentage)
|
||||
public string UpateLockingState(
|
||||
string bikeId,
|
||||
lock_state state,
|
||||
LocationDto geolocation,
|
||||
double batteryPercentage)
|
||||
=> $"request=booking_update&bike={bikeId}{GetLocationParameters(geolocation)}&lock_state={state}{GetBatteryPercentageParameters(batteryPercentage)}&authcookie={SessionCookie}{MerchantId}";
|
||||
|
||||
/// <summary> Gets booking request request (synonym: booking == renting == mieten). </summary>
|
||||
|
@ -119,21 +123,37 @@ namespace TINK.Repository.Request
|
|||
public string DoBook(string bikeId, Guid guid, double batteryPercentage)
|
||||
=> $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&Ilockit_GUID={guid}&state=occupied&lock_state=unlocked{GetBatteryPercentageParameters(batteryPercentage)}";
|
||||
|
||||
/// <summary> Gets the request to book and start opening the bike (synonym: booking == renting == mieten). </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <returns>Request to booking bike.</returns>
|
||||
public string BookAndStartOpening(string bikeId)
|
||||
=> $"request=booking_request&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&state=occupied&lock_state={lock_state.unlocking}";
|
||||
|
||||
/// <summary> Gets request for returning the bike. </summary>
|
||||
/// <remarks> Operator specific call.</remarks>
|
||||
/// <param name="bikeId">Id of bike to return.</param>
|
||||
/// <param name="geolocation">Geolocation of lock when returning bike.</param>
|
||||
/// <returns>Requst on returning request.</returns>
|
||||
public string DoReturn(string bikeId, LocationDto geolocation, ISmartDevice smartDevice)
|
||||
{
|
||||
return $"request=booking_update" +
|
||||
=> $"request=booking_update" +
|
||||
$"&bike={bikeId}" +
|
||||
$"&authcookie={SessionCookie}{MerchantId}" +
|
||||
$"&state=available" +
|
||||
$"{GetLocationParameters(geolocation)}" +
|
||||
$"&lock_state=locked" +
|
||||
$"{GetSmartDeviceParameters(smartDevice)}";
|
||||
}
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
/// <returns>Response to send to corpi.</returns>
|
||||
public string ReturnAndStartClosing(string bikeId, ISmartDevice smartDevice)
|
||||
=> $"request=booking_update" +
|
||||
$"&bike={bikeId}" +
|
||||
$"&authcookie={SessionCookie}{MerchantId}" +
|
||||
$"&state=available" +
|
||||
$"&lock_state={lock_state.locking}" +
|
||||
$"{GetSmartDeviceParameters(smartDevice)}";
|
||||
|
||||
/// <summary> Gets submit feedback request. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TINK.Repository.Response
|
||||
{
|
||||
|
@ -50,8 +50,9 @@ namespace TINK.Repository.Response
|
|||
/// <table>
|
||||
/// <tr><th>Value </th><th>Type of bike </th><th>Member to extract info.</th></tr>
|
||||
/// <tr><td>LOCK </td><td>Bike with manual lock. </td><td>TextToTypeHelper.GetIsNonBikeComputerBike</td></tr>
|
||||
/// <tr><td> </td><td>Bike with a bord computer. </td><td></td></tr>
|
||||
/// <tr><td>? </td><td>Bike with a bluetooth lock.</td><td></td></tr>
|
||||
/// <tr><td>BC </td><td>Bike with a bord computer. </td><td></td></tr>
|
||||
/// <tr><td>Ilockit </td><td>Bike with a bluetooth lock.</td><td></td></tr>
|
||||
/// <tr><td>sigo </td><td>Sigo bike.</td><td></td></tr>
|
||||
/// </table>
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
|
@ -66,6 +67,10 @@ namespace TINK.Repository.Response
|
|||
[DataMember]
|
||||
public string bike_charge { get; private set; }
|
||||
|
||||
/// <summary> Locking state of the bike. </summary>
|
||||
[DataMember]
|
||||
public string lock_state { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Textual description of response.
|
||||
/// </summary>
|
||||
|
|
|
@ -234,11 +234,16 @@ namespace TINK.Model.Services.CopriApi
|
|||
|
||||
public async Task<ReservationBookingResponse> UpdateLockingStateAsync(
|
||||
string bikeId,
|
||||
LocationDto location,
|
||||
lock_state state,
|
||||
double batteryLevel,
|
||||
Uri operatorUri)
|
||||
=> await HttpsServer.UpdateLockingStateAsync(bikeId, location, state, batteryLevel, operatorUri);
|
||||
Uri operatorUri,
|
||||
LocationDto location,
|
||||
double batteryLevel)
|
||||
=> await HttpsServer.UpdateLockingStateAsync(
|
||||
bikeId,
|
||||
state,
|
||||
operatorUri,
|
||||
location,
|
||||
batteryLevel);
|
||||
|
||||
/// <summary> Books a bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
|
@ -246,9 +251,17 @@ namespace TINK.Model.Services.CopriApi
|
|||
/// <param name="batteryPercentage">Holds the filling level percentage of the battery.</param>
|
||||
/// <returns>Response on booking request.</returns>
|
||||
public async Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
|
||||
{
|
||||
return await HttpsServer.DoBookAsync(bikeId, guid, batteryPercentage, operatorUri);
|
||||
}
|
||||
=> await HttpsServer.DoBookAsync(bikeId, guid, batteryPercentage, operatorUri);
|
||||
|
||||
/// <summary> Books a bike and starts opening bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on booking request.</returns>
|
||||
public async Task<ReservationBookingResponse> BookAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> await HttpsServer.BookAndStartOpeningAsync(bikeId, operatorUri);
|
||||
|
||||
|
||||
public async Task<DoReturnResponse> DoReturn(
|
||||
string bikeId,
|
||||
|
@ -257,6 +270,17 @@ namespace TINK.Model.Services.CopriApi
|
|||
Uri operatorUri)
|
||||
=> await HttpsServer.DoReturn(bikeId, location, smartDevice, operatorUri);
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
|
||||
/// <returns>Response on returning request.</returns>
|
||||
public async Task<DoReturnResponse> ReturnAndStartClosingAsync(
|
||||
string bikeId,
|
||||
ISmartDevice smartDevice,
|
||||
Uri operatorUri)
|
||||
=> await HttpsServer.ReturnAndStartClosingAsync(bikeId, smartDevice, operatorUri);
|
||||
|
||||
/// <summary>
|
||||
/// Submits feedback to copri server.
|
||||
/// </summary>
|
||||
|
|
|
@ -48,17 +48,27 @@ namespace TINK.Model.Services.CopriApi
|
|||
|
||||
public async Task<ReservationBookingResponse> UpdateLockingStateAsync(
|
||||
string bikeId,
|
||||
LocationDto geolocation,
|
||||
lock_state state,
|
||||
double batteryLevel,
|
||||
Uri operatorUri)
|
||||
=> await monkeyStore.UpdateLockingStateAsync(bikeId, geolocation, state, batteryLevel, operatorUri);
|
||||
Uri operatorUri,
|
||||
LocationDto geolocation,
|
||||
double batteryLevel)
|
||||
=> await monkeyStore.UpdateLockingStateAsync(
|
||||
bikeId,
|
||||
state,
|
||||
operatorUri,
|
||||
geolocation,
|
||||
batteryLevel);
|
||||
|
||||
public async Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
|
||||
{
|
||||
return await monkeyStore.DoBookAsync(bikeId, guid, batteryPercentage, operatorUri);
|
||||
}
|
||||
|
||||
public async Task<ReservationBookingResponse> BookAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> await monkeyStore.BookAndStartOpeningAsync(bikeId, operatorUri);
|
||||
|
||||
public async Task<DoReturnResponse> DoReturn(
|
||||
string bikeId,
|
||||
LocationDto geolocation,
|
||||
|
@ -66,6 +76,12 @@ namespace TINK.Model.Services.CopriApi
|
|||
Uri operatorUri)
|
||||
=> await monkeyStore.DoReturn(bikeId, geolocation, smartDevice, operatorUri);
|
||||
|
||||
public async Task<DoReturnResponse> ReturnAndStartClosingAsync(
|
||||
string bikeId,
|
||||
ISmartDevice smartDevice,
|
||||
Uri operatorUri)
|
||||
=> await monkeyStore.ReturnAndStartClosingAsync(bikeId, smartDevice, operatorUri);
|
||||
|
||||
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, string messge, bool bIsBikeBroke, Uri operatorUri)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
|
|
209
TINKLib/Services/CopriApi/Polling.cs
Normal file
209
TINKLib/Services/CopriApi/Polling.cs
Normal file
|
@ -0,0 +1,209 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Repository;
|
||||
using TINK.Repository.Response;
|
||||
|
||||
namespace TINK.Services.CopriApi
|
||||
{
|
||||
public static class Polling
|
||||
{
|
||||
/// <summary> Timeout for open/ close operations.</summary>
|
||||
private const int OPEN_CLOSE_TIMEOUT_MS = 50000;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a bike and closes the lock.
|
||||
/// </summary>
|
||||
/// <param name="corpiServer"> Instance to communicate with backend.</param>
|
||||
/// <param name="bike">Bike to open.</param>
|
||||
public static async Task OpenAync(
|
||||
this ICopriServerBase corpiServer,
|
||||
IBikeInfoMutable bike)
|
||||
{
|
||||
if (!(corpiServer is ICachedCopriServer cachedServer))
|
||||
throw new ArgumentNullException(nameof(corpiServer));
|
||||
|
||||
// Send command to close lock
|
||||
await corpiServer.UpdateLockingStateAsync(
|
||||
bike.Id,
|
||||
Repository.Request.lock_state.unlocking,
|
||||
bike.OperatorUri);
|
||||
|
||||
var lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
|
||||
while (lockingState != LockingState.Open
|
||||
&& lockingState != LockingState.UnknownDisconnected
|
||||
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
||||
{
|
||||
// Delay a litte to reduce load on backend.
|
||||
await Task.Delay(3000);
|
||||
|
||||
lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
Log.Information($"Current lock state is {lockingState}.");
|
||||
}
|
||||
|
||||
// Update locking state.
|
||||
bike.LockInfo.State = lockingState;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Books a bike and opens the lock.
|
||||
/// </summary>
|
||||
/// <param name="corpiServer"> Instance to communicate with backend.</param>
|
||||
/// <param name="bike">Bike to book and open.</param>
|
||||
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
||||
public static async Task BookAndOpenAync(
|
||||
this ICopriServerBase corpiServer,
|
||||
IBikeInfoMutable bike,
|
||||
string mailAddress)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bike), "Can not book bike and open lock. No bike object available.");
|
||||
}
|
||||
|
||||
if (!(corpiServer is ICachedCopriServer cachedServer))
|
||||
throw new ArgumentNullException(nameof(corpiServer));
|
||||
|
||||
// Send command to open lock
|
||||
var response = (await corpiServer.BookAndStartOpeningAsync(bike.Id, bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
|
||||
// Upate booking state
|
||||
bike.Load(
|
||||
response,
|
||||
mailAddress,
|
||||
Model.Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
|
||||
// Upated locking state.
|
||||
var lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
|
||||
|
||||
while (lockingState!= LockingState.Open
|
||||
&& lockingState != LockingState.UnknownDisconnected
|
||||
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
||||
{
|
||||
// Delay a litte to reduce load on backend.
|
||||
await Task.Delay(3000);
|
||||
|
||||
lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
Log.Information($"Current lock state is {lockingState}.");
|
||||
}
|
||||
|
||||
// Update locking state.
|
||||
bike.LockInfo.State = lockingState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a bike and closes the lock.
|
||||
/// </summary>
|
||||
/// <param name="corpiServer"> Instance to communicate with backend.</param>
|
||||
/// <param name="bike">Bike to close.</param>
|
||||
public static async Task CloseAync(
|
||||
this ICopriServerBase corpiServer,
|
||||
IBikeInfoMutable bike)
|
||||
{
|
||||
if (!(corpiServer is ICachedCopriServer cachedServer))
|
||||
throw new ArgumentNullException(nameof(corpiServer));
|
||||
|
||||
// Send command to close lock
|
||||
await corpiServer.UpdateLockingStateAsync(
|
||||
bike.Id,
|
||||
Repository.Request.lock_state.locking,
|
||||
bike.OperatorUri);
|
||||
|
||||
var lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
|
||||
while (lockingState != LockingState.Closed
|
||||
&& lockingState != LockingState.UnknownDisconnected
|
||||
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
||||
{
|
||||
// Delay a litte to reduce load on backend.
|
||||
await Task.Delay(3000);
|
||||
|
||||
lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
Log.Information($"Current lock state is {lockingState}.");
|
||||
}
|
||||
|
||||
// Update locking state.
|
||||
bike.LockInfo.State = lockingState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a bike and closes the lock.
|
||||
/// </summary>
|
||||
/// <param name="corpiServer"> Instance to communicate with backend.</param>
|
||||
/// <param name="smartDevice">Smart device on which app runs on.</param>
|
||||
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
||||
public static async Task<BookingFinishedModel> ReturnAndCloseAync(
|
||||
this ICopriServerBase corpiServer,
|
||||
ISmartDevice smartDevice,
|
||||
IBikeInfoMutable bike)
|
||||
{
|
||||
if (!(corpiServer is ICachedCopriServer cachedServer))
|
||||
throw new ArgumentNullException(nameof(corpiServer));
|
||||
|
||||
// Send command to open lock
|
||||
DoReturnResponse response =
|
||||
await corpiServer.ReturnAndStartClosingAsync(bike.Id, smartDevice, bike.OperatorUri);
|
||||
|
||||
// Upate booking state
|
||||
bike.Load(Model.Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
|
||||
var lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
|
||||
while (lockingState != LockingState.Closed
|
||||
&& lockingState != LockingState.UnknownDisconnected
|
||||
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
||||
{
|
||||
// Delay a litte to reduce load on backend.
|
||||
await Task.Delay(3000);
|
||||
|
||||
lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
||||
Log.Information($"Current lock state is {lockingState}.");
|
||||
}
|
||||
|
||||
// Update locking state.
|
||||
bike.LockInfo.State = lockingState;
|
||||
|
||||
return response?.Create() ?? new BookingFinishedModel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the locking state from copri.
|
||||
/// </summary>
|
||||
/// <param name="corpiServer">Service to use.</param>
|
||||
/// <param name="bikeId">Bike id to query lock state for.</param>
|
||||
/// <returns>Locking state</returns>
|
||||
private static async Task<LockingState> GetLockStateAsync(
|
||||
this ICachedCopriServer corpiServer,
|
||||
string bikeId)
|
||||
{
|
||||
var bike = (await corpiServer.GetBikesOccupied(false))?.Response.bikes_occupied?.Values?.FirstOrDefault(x => x.bike == bikeId);
|
||||
|
||||
if (bike == null)
|
||||
return LockingState.UnknownDisconnected;
|
||||
|
||||
return bike.GetCopriLockingState();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,8 +24,8 @@
|
|||
<Warning Text="$(MSBuildProjectFile) is Multilingual build enabled, but the Multilingual App Toolkit is unavailable during the build. If building with Visual Studio, please check to ensure that toolkit is properly installed." />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MonkeyCache" Version="1.5.2" />
|
||||
<PackageReference Include="MonkeyCache.FileStore" Version="1.5.2" />
|
||||
<PackageReference Include="MonkeyCache" Version="1.6.3" />
|
||||
<PackageReference Include="MonkeyCache.FileStore" Version="1.6.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Plugin.BLE" Version="2.1.2" />
|
||||
<PackageReference Include="Plugin.BluetoothLE" Version="6.3.0.19" />
|
||||
|
@ -43,8 +43,8 @@
|
|||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity" Version="3.2.0" />
|
||||
<PackageReference Include="Xam.Plugins.Messaging" Version="5.2.0" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2196" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.2" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2401" />
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps" Version="3.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -55,6 +55,9 @@
|
|||
<Folder Include="Services\Permissions\Plugin\" />
|
||||
<Folder Include="ViewModel\Info\BikeInfo\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Model\Bikes\Bike\CopriLock\ILockInfoMutable.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LockItBLE\LockItBLE.csproj" />
|
||||
<ProjectReference Include="..\LockItShared\LockItShared.csproj" />
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC
|
|||
public override void OnSelectedBikeStateChanged ()
|
||||
{
|
||||
RequestHandler = RequestHandlerFactory.Create(
|
||||
bike,
|
||||
Bike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
ViewUpdateManager,
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// <summary>
|
||||
/// Holds the bike to display.
|
||||
/// </summary>
|
||||
protected BikeInfoMutable bike;
|
||||
protected BikeInfoMutable Bike;
|
||||
|
||||
/// <summary> Reference on the user </summary>
|
||||
protected IUser ActiveUser { get; }
|
||||
|
@ -117,7 +117,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
|
||||
ViewService = viewService;
|
||||
|
||||
bike = selectedBike
|
||||
Bike = selectedBike
|
||||
?? throw new ArgumentException(string.Format("Can not construct {0}- object, bike object is null.", typeof(BikeViewModelBase)));
|
||||
|
||||
ActiveUser = activeUser
|
||||
|
@ -172,18 +172,18 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// <summary>
|
||||
/// Gets the display name of the bike containing of bike id and type of bike..
|
||||
/// </summary>
|
||||
public string Name => bike.GetDisplayName();
|
||||
public string Name => Bike.GetDisplayName();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique Id of bike or an empty string, if no name is defined to avoid duplicate display of id.
|
||||
/// </summary>
|
||||
public string DisplayId => bike.GetDisplayId();
|
||||
public string DisplayId => Bike.GetDisplayId();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique Id of bike used by derived model to determine which bike to remove.
|
||||
/// </summary>
|
||||
public string Id=> bike.Id;
|
||||
public string Id=> Bike.Id;
|
||||
|
||||
/// <summary>
|
||||
/// Returns status of a bike as text.
|
||||
|
@ -193,7 +193,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
{
|
||||
get
|
||||
{
|
||||
switch (bike.State.Value)
|
||||
switch (Bike.State.Value)
|
||||
{
|
||||
case InUseStateEnum.Disposable:
|
||||
return AppResources.StatusTextAvailable;
|
||||
|
@ -202,45 +202,45 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
if (!ActiveUser.IsLoggedIn)
|
||||
{
|
||||
// Nobody is logged in.
|
||||
switch (bike.State.Value)
|
||||
switch (Bike.State.Value)
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
return GetReservedInfo(
|
||||
bike.State.RemainingTime,
|
||||
bike.StationId,
|
||||
Bike.State.RemainingTime,
|
||||
Bike.StationId,
|
||||
null); // Hide reservation code because no one but active user should see code
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
return GetBookedInfo(
|
||||
bike.State.From,
|
||||
bike.StationId,
|
||||
Bike.State.From,
|
||||
Bike.StationId,
|
||||
null); // Hide reservation code because no one but active user should see code
|
||||
|
||||
default:
|
||||
return string.Format("Unbekannter status {0}.", bike.State.Value);
|
||||
return string.Format("Unbekannter status {0}.", Bike.State.Value);
|
||||
}
|
||||
}
|
||||
|
||||
switch (bike.State.Value)
|
||||
switch (Bike.State.Value)
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
return bike.State.MailAddress == ActiveUser.Mail
|
||||
return Bike.State.MailAddress == ActiveUser.Mail
|
||||
? GetReservedInfo(
|
||||
bike.State.RemainingTime,
|
||||
bike.StationId,
|
||||
bike.State.Code)
|
||||
Bike.State.RemainingTime,
|
||||
Bike.StationId,
|
||||
Bike.State.Code)
|
||||
: "Fahrrad bereits reserviert durch anderen Nutzer.";
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
return bike.State.MailAddress == ActiveUser.Mail
|
||||
return Bike.State.MailAddress == ActiveUser.Mail
|
||||
? GetBookedInfo(
|
||||
bike.State.From,
|
||||
bike.StationId,
|
||||
bike.State.Code)
|
||||
Bike.State.From,
|
||||
Bike.StationId,
|
||||
Bike.State.Code)
|
||||
: "Fahrrad bereits gebucht durch anderen Nutzer.";
|
||||
|
||||
default:
|
||||
return string.Format("Unbekannter status {0}.", bike.State.Value);
|
||||
return string.Format("Unbekannter status {0}.", Bike.State.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// <summary>
|
||||
/// Exposes the bike state.
|
||||
/// </summary>
|
||||
public InUseStateEnum State => bike.State.Value;
|
||||
public InUseStateEnum State => Bike.State.Value;
|
||||
|
||||
/// <summary> Gets the value of property <see cref="State"/> when PropertyChanged was fired. </summary>
|
||||
public InUseStateEnum LastState { get; set; }
|
||||
|
@ -296,7 +296,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
return Color.Default;
|
||||
}
|
||||
|
||||
var l_oSelectedBikeState = bike.State;
|
||||
var l_oSelectedBikeState = Bike.State;
|
||||
switch (l_oSelectedBikeState.Value)
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
|
@ -316,7 +316,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
}
|
||||
|
||||
/// <summary> Holds description about the tarif. </summary>
|
||||
public TariffDescriptionViewModel TariffDescription => new TariffDescriptionViewModel(bike.TariffDescription);
|
||||
public TariffDescriptionViewModel TariffDescription => new TariffDescriptionViewModel(Bike.TariffDescription);
|
||||
|
||||
/// <summary> Gets the value of property <see cref="StateColor"/> when PropertyChanged was fired. </summary>
|
||||
public Color LastStateColor { get; set; }
|
||||
|
|
|
@ -29,8 +29,9 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
IBikesViewModel bikesViewModel,
|
||||
Action<string> openUrlInBrowser)
|
||||
{
|
||||
return bikeInfo as Model.Bikes.Bike.BluetoothLock.IBikeInfoMutable != null
|
||||
? new BluetoothLock.BikeViewModel(
|
||||
if (bikeInfo is Model.Bike.BluetoothLock.BikeInfoMutable)
|
||||
{
|
||||
return new BluetoothLock.BikeViewModel(
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -43,10 +44,14 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
activeUser,
|
||||
stateInfoProvider,
|
||||
bikesViewModel,
|
||||
openUrlInBrowser) as BikeViewModelBase
|
||||
: new BC.BikeViewModel(
|
||||
openUrlInBrowser);
|
||||
}
|
||||
if (bikeInfo is Model.Bike.CopriLock.BikeInfoMutable)
|
||||
{
|
||||
return new CopriLock.BikeViewModel(
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
bikeRemoveDelegate,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
|
@ -56,6 +61,9 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
stateInfoProvider,
|
||||
bikesViewModel,
|
||||
openUrlInBrowser);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
{
|
||||
var lastHandler = RequestHandler;
|
||||
RequestHandler = RequestHandlerFactory.Create(
|
||||
bike,
|
||||
Bike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
Geolocation,
|
||||
|
|
|
@ -209,7 +209,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
|
@ -533,7 +533,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
|
|
|
@ -253,7 +253,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
|
|
|
@ -53,12 +53,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReserverBookAndOpen();
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReserveBookAndOpen();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> ReserverBookAndOpen()
|
||||
public async Task<IRequestHandler> ReserveBookAndOpen()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
/// <summary> Open lock and book bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLockAndDooBook();
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLockAndDoBook();
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> CancelReservation()
|
||||
|
@ -160,18 +160,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
/// <summary> Open lock and book bike. </summary>
|
||||
public async Task<IRequestHandler> OpenLockAndDooBook()
|
||||
public async Task<IRequestHandler> OpenLockAndDoBook()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {bike} in order to book but action was canceled.", SelectedBike);
|
||||
|
|
|
@ -188,7 +188,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorCloseLockOutOfReachStateReservedMessage,
|
||||
"OK");
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock bike is moving. {Exception}", exception);
|
||||
|
||||
|
|
|
@ -253,7 +253,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
|
|
189
TINKLib/ViewModel/Bikes/Bike/CopriLock/BikeViewModel.cs
Normal file
189
TINKLib/ViewModel/Bikes/Bike/CopriLock/BikeViewModel.cs
Normal file
|
@ -0,0 +1,189 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
/// <summary>
|
||||
/// View model for a ILockIt bike.
|
||||
/// Provides functionality for views
|
||||
/// - MyBikes
|
||||
/// - BikesAtStation
|
||||
/// </summary>
|
||||
public class BikeViewModel : BikeViewModelBase, INotifyPropertyChanged
|
||||
{
|
||||
/// <summary> Notifies GUI about changes. </summary>
|
||||
public override event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private IGeolocation Geolocation { get; }
|
||||
|
||||
/// <summary> Holds object which manages requests. </summary>
|
||||
private IRequestHandler RequestHandler { get; set; }
|
||||
|
||||
/// <summary> Raises events in order to update GUI.</summary>
|
||||
public override void RaisePropertyChanged(object sender, PropertyChangedEventArgs eventArgs) => PropertyChanged?.Invoke(sender, eventArgs);
|
||||
|
||||
/// <summary> Raises events if property values changed in order to update GUI.</summary>
|
||||
private void RaisePropertyChangedEvent(
|
||||
IRequestHandler lastHandler,
|
||||
string lastStateText = null,
|
||||
Xamarin.Forms.Color? lastStateColor = null)
|
||||
{
|
||||
if (lastHandler.ButtonText != ButtonText)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonText)));
|
||||
}
|
||||
|
||||
if (lastHandler.IsButtonVisible != IsButtonVisible)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsButtonVisible)));
|
||||
}
|
||||
|
||||
if (lastHandler.LockitButtonText != LockitButtonText)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LockitButtonText)));
|
||||
}
|
||||
|
||||
if (lastHandler.IsLockitButtonVisible != IsLockitButtonVisible)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLockitButtonVisible)));
|
||||
}
|
||||
|
||||
if (RequestHandler.ErrorText != lastHandler.ErrorText)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ErrorText)));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(lastStateText) && lastStateText != StateText)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StateText)));
|
||||
}
|
||||
|
||||
if (lastStateColor != null && lastStateColor != StateColor)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StateColor)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bike view model object.
|
||||
/// </summary>
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="selectedBike">Bike to be displayed.</param>
|
||||
/// <param name="user">Object holding logged in user or an empty user object.</param>
|
||||
/// <param name="stateInfoProvider">Provides in use state information.</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
/// <param name="openUrlInBrowser">Delegate to open browser.</param>
|
||||
public BikeViewModel(
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Action<string> bikeRemoveDelegate,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
BikeInfoMutable selectedBike,
|
||||
IUser user,
|
||||
IInUseStateInfoProvider stateInfoProvider,
|
||||
IBikesViewModel bikesViewModel,
|
||||
Action<string> openUrlInBrowser) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, user, stateInfoProvider, bikesViewModel, openUrlInBrowser)
|
||||
{
|
||||
RequestHandler = user.IsLoggedIn
|
||||
? RequestHandlerFactory.Create(
|
||||
selectedBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
user)
|
||||
: new BluetoothLock.NotLoggedIn(
|
||||
selectedBike.State.Value,
|
||||
viewService,
|
||||
bikesViewModel);
|
||||
|
||||
Geolocation = geolocation
|
||||
?? throw new ArgumentException($"Can not instantiate {this.GetType().Name}-object. Parameter {nameof(geolocation)} can not be null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles BikeInfoMutable events.
|
||||
/// Helper member to raise events. Maps model event change notification to view model events.
|
||||
/// Todo: Check which events are received here and filter, to avoid event storm.
|
||||
/// </summary>
|
||||
public override void OnSelectedBikeStateChanged()
|
||||
{
|
||||
var lastHandler = RequestHandler;
|
||||
RequestHandler = RequestHandlerFactory.Create(
|
||||
Bike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
Geolocation,
|
||||
ViewUpdateManager,
|
||||
SmartDevice,
|
||||
ViewService,
|
||||
BikesViewModel,
|
||||
ActiveUser);
|
||||
|
||||
RaisePropertyChangedEvent(lastHandler);
|
||||
}
|
||||
|
||||
/// <summary> Gets visiblity of the copri command button. </summary>
|
||||
public bool IsButtonVisible => RequestHandler.IsButtonVisible;
|
||||
|
||||
/// <summary> Gets the text of the copri command button. </summary>
|
||||
public string ButtonText => RequestHandler.ButtonText;
|
||||
|
||||
/// <summary> Gets visiblity of the ILockIt command button. </summary>
|
||||
public bool IsLockitButtonVisible => RequestHandler.IsLockitButtonVisible;
|
||||
|
||||
/// <summary> Gets the text of the ILockIt command button. </summary>
|
||||
public string LockitButtonText => RequestHandler.LockitButtonText;
|
||||
|
||||
/// <summary> Processes request to perform a copri action (reserve bike and cancel reservation). </summary>
|
||||
public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption1()));
|
||||
|
||||
/// <summary> Processes request to perform a ILockIt action (unlock bike and lock bike). </summary>
|
||||
public System.Windows.Input.ICommand OnLockitButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption2()));
|
||||
|
||||
/// <summary> Processes request to perform a copri action (reserve bike and cancel reservation). </summary>
|
||||
private async Task ClickButton(Task<IRequestHandler> handleRequest)
|
||||
{
|
||||
var lastHandler = RequestHandler;
|
||||
var lastStateText = StateText;
|
||||
var lastStateColor = StateColor;
|
||||
|
||||
RequestHandler = await handleRequest;
|
||||
|
||||
if (lastHandler.IsRemoveBikeRequired)
|
||||
{
|
||||
BikeRemoveDelegate(Id);
|
||||
}
|
||||
|
||||
if (RuntimeHelpers.Equals(lastHandler, RequestHandler))
|
||||
{
|
||||
// No state change occurred (same instance is returned).
|
||||
return;
|
||||
}
|
||||
|
||||
RaisePropertyChangedEvent(
|
||||
lastHandler,
|
||||
lastStateText,
|
||||
lastStateColor);
|
||||
}
|
||||
|
||||
public string ErrorText => RequestHandler.ErrorText;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.Bike.CopriLock.IBikeInfoMutable>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs the reqest handler base.
|
||||
/// </summary>
|
||||
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param>
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public Base(
|
||||
Model.Bikes.Bike.CopriLock.IBikeInfoMutable selectedBike,
|
||||
string buttonText,
|
||||
bool isCopriButtonVisible,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(selectedBike, buttonText, isCopriButtonVisible, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
|
||||
{
|
||||
Geolocation = geolocation
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. Parameter {nameof(geolocation)} must not be null.");
|
||||
}
|
||||
|
||||
protected IGeolocation Geolocation { get; }
|
||||
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public abstract override InUseStateEnum State { get; }
|
||||
|
||||
public string LockitButtonText { get; protected set; }
|
||||
|
||||
public bool IsLockitButtonVisible { get; protected set; }
|
||||
|
||||
public string ErrorText => string.Empty;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Services.Geolocation;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Services.CopriLock.Exception;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
public class BookedClosed : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public BookedClosed(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionReturn, // Copri button text "Miete beenden"
|
||||
true, // Show button to enabled returning of bike.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndPause;
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReturnBike();
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLock();
|
||||
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> ReturnBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
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);
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<BookedClosed>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = "Returning bike...";
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
BookingFinishedModel bookingFinished;
|
||||
try
|
||||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(SelectedBike);
|
||||
|
||||
// If canceling bike succedes remove bike because it is not ready to be booked again
|
||||
IsRemoveBikeRequired = true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
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)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an not at station error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockClosedNoGPSMessage),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
"OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedClosed>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(bookingFinished?.Co2Saving);
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message },
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
#endif
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedClosed>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.OpenLockAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
AppResources.ErrorOpenLockBoldWasBlockedMessage,
|
||||
"OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
"OK");
|
||||
}
|
||||
|
||||
// When bold is blocked lock is still closed even if exception occurres.
|
||||
// In all other cases state is supposed to be unknown. Example: Lock is out of reach and no more bluetooth connected.
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedClosed>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.CopriLock;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.View;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using System.Linq;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
public class BookedDefault : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public BookedDefault(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) :
|
||||
base(
|
||||
selectedBike,
|
||||
nameof(BookedDefault),
|
||||
false,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
||||
/// <summary> Scan for lock.</summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ConnectLock();
|
||||
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
Log.ForContext<BookedDefault>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
|
||||
/// <summary> Scan for lock.</summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> ConnectLock()
|
||||
{
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedDefault>().Information("Request to search {bike} detected.", SelectedBike);
|
||||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
var bikesInfo = (await ConnectorFactory(IsConnected).Query.GetBikesOccupiedAsync())?.Response?.Where(bike => bike.Id == SelectedBike.Id).ToArray();
|
||||
|
||||
var bikeInfo = bikesInfo.Length > 0 ? bikesInfo[0] as BikeInfo : null;
|
||||
SelectedBike.LockInfo.State = bikeInfo != null ? bikeInfo.LockInfo.State: SelectedBike.LockInfo.State;
|
||||
|
||||
Log.ForContext<BookedDefault>().Information($"State for bike {SelectedBike.Id} updated successfully. Value is {SelectedBike.LockInfo.State}.");
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Services.CopriLock.Exception;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
public class BookedOpen : Base, IRequestHandler
|
||||
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public BookedOpen(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
Services.Geolocation.IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseAndReturn, // Copri button text: "Schloss schließen & Miete beenden"
|
||||
true, // Show button to allow user to return bike.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen".
|
||||
IsLockitButtonVisible = true; // Show button to allow user to lock bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockAndReturnBike();
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> CloseLockAndReturnBike()
|
||||
{
|
||||
// Prevent concurrent interaction
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
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);
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedOpen>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.ReturnAndCloseAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.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, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
if (SelectedBike.LockInfo.State != LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error($"Lock can not be closed. Invalid locking state state {SelectedBike.LockInfo.State} detected.");
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
SelectedBike.LockInfo.State == LockingState.Open
|
||||
? AppResources.ErrorCloseLockStillOpenMessage
|
||||
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
|
||||
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, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
BookingFinishedModel bookingFinished;
|
||||
try
|
||||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
smartDevice: SmartDevice);
|
||||
// If canceling bike succedes remove bike because it is not ready to be booked again
|
||||
IsRemoveBikeRequired = true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
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)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockOpenNoGPSMessage),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(bookingFinished?.Co2Saving);
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message },
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <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<BookedOpen>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.CloseLockAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
using System;
|
||||
using Serilog;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
public class DisposableClosed : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...).</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public DisposableClosed(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndBook, // Button text: "Schloss öffnen & Rad mieten"
|
||||
true, // Show copri button to enable reserving and opening
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false; // If bike is not reserved/ booked app can not connect to lock
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await BookAndOpen();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> BookAndOpen()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposableClosed>().Information("User selected recently requested bike {bike} in order to reserve but did deny to book bike.", SelectedBike);
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableClosed>().Information("User selected recently requested bike {bike} in order to book.", SelectedBike);
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.BookAndOpenAync(SelectedBike);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableClosed>().Information("User selected recently requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableClosed>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Empty,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
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, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
Log.ForContext<DisposableClosed>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue