Version 3.0.366

This commit is contained in:
Anja 2023-06-06 12:00:24 +02:00
parent 0eb7362cb8
commit 24cdfbb0ca
84 changed files with 900 additions and 393 deletions

View file

@ -12,3 +12,4 @@ sharee
tink tink
ui ui
xdoc xdoc
xml

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?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.365" android:versionCode="365"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.TeilRad.LastenradBayern" android:versionName="3.0.366" android:versionCode="366">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" /> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
<!-- Google Maps related permissions --> <!-- Google Maps related permissions -->
<!-- Permission to receive remote notifications from Google Play Services --> <!-- Permission to receive remote notifications from Google Play Services -->

View file

@ -56,8 +56,8 @@
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>LastenradBayern</string> <string>LastenradBayern</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>365</string> <string>366</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.365</string> <string>3.0.366</string>
</dict> </dict>
</plist> </plist>

View file

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html xml:lang="de" lang="de">
<title>TINK Konstanz</title>
<meta charset="utf-8"/>
<body>
<h1>WIE FUNKTIONIERT DAS TRANSPORTRAD-MIETEN?</h1>
<h2>Erstmalige Registrierung</h2><br>
Vor der ersten Anmietung eines Transportrades ist es notwendig, sich
kostenlos als Nutzerin oder Nutzer zu registrieren. Das dauert nur
wenige Minuten und geht am einfachsten über den <a href="https://tink-konstanz.de/TINK-Konstanz/Mieten">Mieten</a> Button. Sobald der Account freigeschaltet ist, kann es losgehen.<br><br>
<h2>Transportrad mieten</h2><br>
Einfach zur nächsten TINK Station gehen (freie Räder siehe Karte) und den Code
für das Zahlenschloss mittels SMS anfordern. Die Anleitung, wie es genau
geht, findet sich direkt an den Stationen, auf den Rädern oder hier: <br><a href="https://tink-konstanz.de/TINK-Konstanz/Anleitungen#3401">
<li>Anleitung Mietvorgang</a><br><a href="https://tink-konstanz.de/TINK-Konstanz/Anleitungen#3402">
<li>Anleitung TINK Räder</a><br>
<span style="font-weight:bold"> <li>Wichtig:</span> Nach der Nutzung das Rad an eine der TINK Stationen zurückbringen, an der Station anschließen und mittels SMS ausloggen.<br><br>
Die erste Version der TINK APP ist verfügbar. Wir freuen uns auf konstruktives feedback.
<br><br><h2>Preise</h2><br>
Die erste Stunde pro Tag ist kostenfrei, danach kostet jede weitere halbe Stunde 1 Euro. Maximal
kostet ein Rad pro 24 Stunden 9 Euro. Es kann ein Rad pro Account
gemietet werden. Bezahlung per Abbuchung oder Kreditkarte.<br>Servicegebühren:
Bei Abstellen eines Rades außerhalb der Stationen werden
entfernungsabhängige Gebühren für die Rückführung berechnet. Aktuelle
Preisliste siehe AGBs.</div>
<h1>WELCHE TRANSPORTRÄDER GIBT ES BEI TINK?</h1>
<div class="content2"><span class="content1">Zweirädriges Transportrad mit Platz für zwei Getränkekisten, Zuladung bis 80 kg. <br>Dreirädriges Transportrad sogar mit Platz für vier Getränkekisten, Zuladung bis 100 kg.</span><br>Jedes
Rad verfügt über eine leichtgängige Achtgang-Schaltung und einen
höhenverstellbaren Sattel. Im Zweirad können 2 Kinder, im Dreirad sogar 4
Kinder bis 6 Jahre mitgenommen werden. Die wegklappbaren Kindersitze
verfügen über Sicherheitsgurte.<br>Die Räder sind nach etwas Gewöhnung
leicht und sicher zu fahren. Vor der ersten Nutzung empfehlen wir ein
kurzes Üben ohne Beladung abseits des Straßenverkehrs. Einfach mal
ausprobieren, es macht richtig Spaß!
</body>
</html>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?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.365" android:versionCode="365"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.TeilRad.Meinkonrad" android:versionName="3.0.366" android:versionCode="366">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" /> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
<!-- Google Maps related permissions --> <!-- Google Maps related permissions -->
<!-- Permission to receive remote notifications from Google Play Services --> <!-- Permission to receive remote notifications from Google Play Services -->

View file

@ -56,8 +56,8 @@
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Mein konrad</string> <string>Mein konrad</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>365</string> <string>366</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.365</string> <string>3.0.366</string>
</dict> </dict>
</plist> </plist>

View file

@ -2,6 +2,7 @@
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.FindBike.FindBikePage" x:Class="TINK.View.FindBike.FindBikePage"
xmlns:conv="clr-namespace:TINK.View"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib" xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
xmlns:local_bike="clr-namespace:TINK.View.Bike" xmlns:local_bike="clr-namespace:TINK.View.Bike"
xmlns:sharedGui="clr-namespace:ShareeSharedGuiLib.View" xmlns:sharedGui="clr-namespace:ShareeSharedGuiLib.View"
@ -17,6 +18,7 @@
<ContentPage.Resources> <ContentPage.Resources>
<ResourceDictionary> <ResourceDictionary>
<local_bike:BikeViewCellTemplateSelector x:Key="bikeTemplateSelector"/> <local_bike:BikeViewCellTemplateSelector x:Key="bikeTemplateSelector"/>
<conv:StringNotNullOrEmptyToVisibleConverter x:Key="Label_Converter"/>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>
@ -25,56 +27,101 @@
<!--Grid for Bike(s) view and Running process in same row--> <!--Grid for Bike(s) view and Running process in same row-->
<Grid> <Grid>
<!-- Grid for Content -->
<Grid <Grid
RowDefinitions="1*,Auto" RowDefinitions="1*,Auto"
RowSpacing="0" RowSpacing="0"
Grid.Row="0"> Grid.Row="0">
<StackLayout <StackLayout
Grid.Row="0" Grid.Row="0"
Spacing="0" Spacing="0"
Orientation="Vertical"> Orientation="Vertical">
<!--No Network Connection--> <StackLayout
<sharedGui:NotConnectedToNetView/> BackgroundColor="White"
Padding="20,5,20,0">
<!--Search bike--> <!--Bike type-->
<Frame <StackLayout Orientation="Horizontal"
Padding="10" HorizontalOptions="Center"
Margin="0,10,0,5" IsVisible="{Binding ActiveFilteredBikeType, Converter={StaticResource Label_Converter}}"
IsVisible="{Binding IsSelectBikeVisible}" Spacing="0">
HorizontalOptions="FillAndExpand" <Label
BackgroundColor="White"> TextColor="DimGray"
Text="{x:Static resources:AppResources.MarkingFindBikeTypeOfBikeText}"/>
<Label
TextColor="{DynamicResource primary-back-title-color}"
FontAttributes="Bold"
Text="{Binding ActiveFilteredBikeType}"/>
<Button
Command="{Binding ShowFilterBikeTypeInfoCommand}"
WidthRequest="24"
HeightRequest="24"
BackgroundColor="Transparent"
BorderWidth="0"
Padding="0"
Margin="5,0,0,0">
<Button.ImageSource>
<FontImageSource
Glyph="{StaticResource InfoCircle}"
Color="DimGray"
FontFamily="FA-S"
Size="20"/>
</Button.ImageSource>
</Button>
</StackLayout>
<StackLayout <Grid
Padding="20"> RowDefinitions="Auto,Auto"
ColumnDefinitions="*,Auto">
<Label Text="{x:Static resources:AppResources.MarkingFindBikeLabel}" <!--Search bike-->
Margin="0,0,0,-5"> <Label
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="0"
Text="{x:Static resources:AppResources.MarkingFindBikeLabel}"
Margin="0,5,0,-5">
<Label.Triggers> <Label.Triggers>
<DataTrigger <DataTrigger
TargetType="Label" TargetType="Label"
Binding="{Binding Source={x:Reference FindBikeEntry}, Path=Text, TargetNullValue=''}" Binding="{Binding Source={x:Reference FindBikeEntry}, Path=Text, TargetNullValue=''}"
Value=""> Value="">
<Setter Property="IsVisible" Value="False" /> <Setter Property="IsVisible" Value="False" />
</DataTrigger> </DataTrigger>
</Label.Triggers> </Label.Triggers>
</Label> </Label>
<Entry <Entry
Grid.Column="0"
Grid.Row="1"
x:Name="FindBikeEntry" x:Name="FindBikeEntry"
Placeholder="{x:Static resources:AppResources.PlaceholderFindBike}" Placeholder="{x:Static resources:AppResources.PlaceholderFindBike}"
MaxLength="10" MaxLength="10"
CursorPosition="0" CursorPosition="0"
Text="{Binding BikeIdUserInput}"/> Text="{Binding BikeIdUserInput, Mode=TwoWay}"/>
<Button <Button
Text="{x:Static resources:AppResources.MarkingSearchBike}" Grid.Column="1"
Grid.Row="1"
WidthRequest="100"
Text="{x:Static resources:AppResources.MarkingFindBikeButton}"
IsEnabled="{Binding IsSelectBikeEnabled}" IsEnabled="{Binding IsSelectBikeEnabled}"
Command="{Binding OnSelectBikeRequest}"/> Command="{Binding OnSelectBikeRequest}"/>
</StackLayout> </Grid>
</Frame> <!--Line-->
<BoxView
HeightRequest="1"
WidthRequest="400"
HorizontalOptions="Center"
Color="{DynamicResource primary-back-title-color}"/>
</StackLayout>
<!--No Network Connection-->
<sharedGui:NotConnectedToNetView/>
<!-- Bike --> <!-- Bike -->
<StackLayout <StackLayout
@ -86,16 +133,16 @@
<sharedGui:HintForRefreshingPageView/> <sharedGui:HintForRefreshingPageView/>
<ListView <ListView
x:Name="FindBikeListView" x:Name="FindBikeListView"
SelectionMode="None" SelectionMode="None"
SelectedItem="{Binding SelectedBike}" SelectedItem="{Binding SelectedBike}"
IsEnabled="{Binding IsIdle}" IsEnabled="{Binding IsIdle}"
HasUnevenRows="True" HasUnevenRows="True"
SeparatorVisibility="None" SeparatorVisibility="None"
ItemTemplate="{StaticResource bikeTemplateSelector}" ItemTemplate="{StaticResource bikeTemplateSelector}"
IsPullToRefreshEnabled="True" IsPullToRefreshEnabled="True"
RefreshCommand="{Binding RefreshCommand}" RefreshCommand="{Binding RefreshCommand}"
IsRefreshing="{Binding IsRefreshing}"/> IsRefreshing="{Binding IsRefreshing}"/>
</StackLayout> </StackLayout>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.hauffware.sharee" android:versionName="3.0.365" android:versionCode="365"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="com.hauffware.sharee" android:versionName="3.0.366" android:versionCode="366">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" /> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
<!-- Google Maps related permissions --> <!-- Google Maps related permissions -->
<!-- Permission to receive remote notifications from Google Play Services --> <!-- Permission to receive remote notifications from Google Play Services -->

View file

@ -56,8 +56,8 @@
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>sharee.bike</string> <string>sharee.bike</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>365</string> <string>366</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.365</string> <string>3.0.366</string>
</dict> </dict>
</plist> </plist>

View file

@ -3,6 +3,7 @@
xmlns="http://xamarin.com/schemas/2014/forms" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.FindBike.FindBikePage" x:Class="TINK.View.FindBike.FindBikePage"
xmlns:conv="clr-namespace:TINK.View"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib" xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
xmlns:local_bike="clr-namespace:TINK.View.Bike" xmlns:local_bike="clr-namespace:TINK.View.Bike"
xmlns:sharedGui="clr-namespace:ShareeSharedGuiLib.View" xmlns:sharedGui="clr-namespace:ShareeSharedGuiLib.View"
@ -18,6 +19,7 @@
<ContentPage.Resources> <ContentPage.Resources>
<ResourceDictionary> <ResourceDictionary>
<local_bike:BikeViewCellTemplateSelector x:Key="bikeTemplateSelector"/> <local_bike:BikeViewCellTemplateSelector x:Key="bikeTemplateSelector"/>
<conv:StringNotNullOrEmptyToVisibleConverter x:Key="Label_Converter"/>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>
@ -26,56 +28,71 @@
<!--Grid for Bike(s) view and Running process in same row--> <!--Grid for Bike(s) view and Running process in same row-->
<Grid> <Grid>
<!-- Grid for Content -->
<Grid <Grid
RowDefinitions="1*,Auto" RowDefinitions="1*,Auto"
RowSpacing="0" RowSpacing="0"
Grid.Row="0"> Grid.Row="0">
<StackLayout <StackLayout
Grid.Row="0" Grid.Row="0"
Spacing="0" Spacing="0"
Orientation="Vertical"> Orientation="Vertical">
<!--No Network Connection--> <StackLayout
<sharedGui:NotConnectedToNetView/> BackgroundColor="White"
Padding="20,0,20,0">
<!--Search bike--> <Grid
<Frame RowDefinitions="Auto,Auto"
Padding="10" ColumnDefinitions="*,Auto">
Margin="0,10,0,5"
IsVisible="{Binding IsSelectBikeVisible}"
HorizontalOptions="FillAndExpand"
BackgroundColor="White">
<StackLayout <!--Search bike-->
Padding="20"> <Label
Grid.Column="0"
<Label Text="{x:Static resources:AppResources.MarkingFindBikeLabel}" Grid.ColumnSpan="2"
Margin="0,0,0,-5"> Grid.Row="0"
Text="{x:Static resources:AppResources.MarkingFindBikeLabel}"
Margin="0,5,0,-5">
<Label.Triggers> <Label.Triggers>
<DataTrigger <DataTrigger
TargetType="Label" TargetType="Label"
Binding="{Binding Source={x:Reference FindBikeEntry}, Path=Text, TargetNullValue=''}" Binding="{Binding Source={x:Reference FindBikeEntry}, Path=Text, TargetNullValue=''}"
Value=""> Value="">
<Setter Property="IsVisible" Value="False" /> <Setter Property="IsVisible" Value="False" />
</DataTrigger> </DataTrigger>
</Label.Triggers> </Label.Triggers>
</Label> </Label>
<Entry <Entry
Grid.Column="0"
Grid.Row="1"
x:Name="FindBikeEntry" x:Name="FindBikeEntry"
Placeholder="{x:Static resources:AppResources.PlaceholderFindBike}" Placeholder="{x:Static resources:AppResources.PlaceholderFindBike}"
MaxLength="10" MaxLength="10"
CursorPosition="0" CursorPosition="0"
Text="{Binding BikeIdUserInput}"/> Text="{Binding BikeIdUserInput, Mode=TwoWay}"/>
<Button <Button
Text="{x:Static resources:AppResources.MarkingSearchBike}" Grid.Column="1"
Grid.Row="1"
WidthRequest="100"
Text="{x:Static resources:AppResources.MarkingFindBikeButton}"
IsEnabled="{Binding IsSelectBikeEnabled}" IsEnabled="{Binding IsSelectBikeEnabled}"
Command="{Binding OnSelectBikeRequest}"/> Command="{Binding OnSelectBikeRequest}"/>
</StackLayout> </Grid>
</Frame> <!--Line-->
<BoxView
HeightRequest="1"
WidthRequest="400"
HorizontalOptions="Center"
Color="{DynamicResource primary-back-title-color}"/>
</StackLayout>
<!--No Network Connection-->
<sharedGui:NotConnectedToNetView/>
<!-- Bike --> <!-- Bike -->
<StackLayout <StackLayout
@ -87,19 +104,19 @@
<sharedGui:HintForRefreshingPageView/> <sharedGui:HintForRefreshingPageView/>
<ListView <ListView
x:Name="FindBikeListView" x:Name="FindBikeListView"
SelectionMode="None" SelectionMode="None"
SelectedItem="{Binding SelectedBike}" SelectedItem="{Binding SelectedBike}"
IsEnabled="{Binding IsIdle}" IsEnabled="{Binding IsIdle}"
HasUnevenRows="True" HasUnevenRows="True"
SeparatorVisibility="None" SeparatorVisibility="None"
ItemTemplate="{StaticResource bikeTemplateSelector}" ItemTemplate="{StaticResource bikeTemplateSelector}"
IsPullToRefreshEnabled="True" IsPullToRefreshEnabled="True"
RefreshCommand="{Binding RefreshCommand}" RefreshCommand="{Binding RefreshCommand}"
IsRefreshing="{Binding IsRefreshing}"/> IsRefreshing="{Binding IsRefreshing}"/>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<!--Info text--> <!--Info text-->

View file

@ -21,7 +21,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
private readonly StateInfoMutable _StateInfo; private readonly StateInfoMutable _StateInfo;
/// <summary> /// <summary>
/// Constructs a bike. /// Constructs a bike info object.
/// </summary> /// </summary>
/// <param name="isDemo">True if device is demo device, false otherwise.</param> /// <param name="isDemo">True if device is demo device, false otherwise.</param>
/// <param name="dateTimeProvider">Provider for current date time to calculate remaining time on demand for state of type reserved.</param> /// <param name="dateTimeProvider">Provider for current date time to calculate remaining time on demand for state of type reserved.</param>
@ -38,7 +38,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
string stationId = null, string stationId = null,
string stationName = null, string stationName = null,
Uri operatorUri = null, Uri operatorUri = null,
RentalDescription tariffDescription = null, IRentalDescription tariffDescription = null,
Func<DateTime> dateTimeProvider = null, Func<DateTime> dateTimeProvider = null,
IStateInfo stateInfo = null) IStateInfo stateInfo = null)
{ {
@ -65,7 +65,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary> Holds description about the tariff. </summary> /// <summary> Holds description about the tariff. </summary>
[DataMember] [DataMember]
public RentalDescription TariffDescription { get; private set; } public IRentalDescription TariffDescription { get; private set; }
/// <summary> /// <summary>
/// Holds the rent state of the bike. /// Holds the rent state of the bike.

View file

@ -64,6 +64,11 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary> Gets or sets the information where the data origins from. </summary> /// <summary> Gets or sets the information where the data origins from. </summary>
DataSource DataSource { get; set; } DataSource DataSource { get; set; }
/// <summary>
/// Gets or set the rental description.
/// </summary>
IRentalDescription TariffDescription { get; }
event PropertyChangedEventHandler PropertyChanged; event PropertyChangedEventHandler PropertyChanged;
} }

View file

@ -83,8 +83,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
new StateInfo( new StateInfo(
dateTimeProvider, dateTimeProvider,
requestedAt, requestedAt,
tariffDescription?.MaxReservationTimeSpan ?? StateRequestedInfo.UNKNOWNMAXRESERVATIONTIMESPAN,
mailAddress, mailAddress,
""), "" ), // BC code
bike != null bike != null
? new Bike( ? new Bike(
bike.Id, bike.Id,

View file

@ -84,8 +84,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
new StateInfo( new StateInfo(
dateTimeProvider, dateTimeProvider,
requestedAt, requestedAt,
tariffDescription?.MaxReservationTimeSpan ?? StateRequestedInfo.UNKNOWNMAXRESERVATIONTIMESPAN,
mailAddress, mailAddress,
""), ""), // BC code
bike != null bike != null
? new Bike( ? new Bike(
bike.Id, bike.Id,

View file

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using static TINK.Model.Bikes.BikeInfoNS.RentalDescription;
namespace TINK.Model.Bikes.BikeInfoNS
{
public interface IRentalDescription
{
/// <summary>
/// Name of the tariff.
/// </summary>
string Name { get; set; }
/// <summary>
/// Holds the time span for which a bike can be reserved.
/// </summary>
TimeSpan MaxReservationTimeSpan { get; set; }
/// <summary>
/// Dynamic language aware tariff elements to be displayed to user.
/// </summary>
Dictionary<string, TariffElement> TariffEntries { get; set; }
/// <summary>
/// Well known language aware elements (AGB, tracking info, ...) to be displayed to user.
/// </summary>
Dictionary<string, InfoElement> InfoEntries { get; set; }
}
}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace TINK.Model.Bikes.BikeInfoNS namespace TINK.Model.Bikes.BikeInfoNS
@ -6,20 +7,20 @@ namespace TINK.Model.Bikes.BikeInfoNS
/// Successor of TarifDescription- object. /// Successor of TarifDescription- object.
/// Manages tariff- and rental info. /// Manages tariff- and rental info.
/// </summary> /// </summary>
public class RentalDescription public class RentalDescription : IRentalDescription
{ {
/// <summary> /// <summary>
/// The different elements of a tariff (example: "Max Gebühr", ) /// The different elements of a tariff (example: "Max Gebühr", ) to be displayed by sharee.bike without processing
/// </summary> /// </summary>
public class TariffElement public class TariffElement
{ {
/// <summary> /// <summary>
/// Describes the tariff element. To be displayed to user (example of elements: "Gratis Mietzeit", "Mietgebühr", "Max Gebühr"). /// Describes the tariff element (language aware). To be displayed to user (example of elements: "Gratis Mietzeit", "Mietgebühr", "Max Gebühr").
/// </summary> /// </summary>
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Holds the tariff element value. To be displayed to user (example: "9.00 € / Tag"). /// Holds the tariff element value (language aware, i.e. value from backend might be english, german, ... depending on smart phone value). To be displayed to user (example: "9.00 € / Tag").
/// </summary> /// </summary>
public string Value { get; set; } = string.Empty; public string Value { get; set; } = string.Empty;
} }
@ -30,12 +31,12 @@ namespace TINK.Model.Bikes.BikeInfoNS
public class InfoElement public class InfoElement
{ {
/// <summary> /// <summary>
/// Key which identyfies the value (required for special processing) /// Key which identifies the value (required for special processing)
/// </summary> /// </summary>
public string Key { get; set; } public string Key { get; set; }
/// <summary> /// <summary>
/// Text to be displayed to user. /// Text (language aware) to be displayed to user.
/// </summary> /// </summary>
public string Value { get; set; } public string Value { get; set; }
} }
@ -50,8 +51,19 @@ namespace TINK.Model.Bikes.BikeInfoNS
/// </summary> /// </summary>
public int? Id { get; set; } public int? Id { get; set; }
/// <summary>
/// Holds the time span for which a bike can be reserved.
/// </summary>
public TimeSpan MaxReservationTimeSpan { get; set; }
/// <summary>
/// Dynamic language aware tariff elements to be displayed to user.
/// </summary>
public Dictionary<string, TariffElement> TariffEntries { get; set; } = new Dictionary<string, TariffElement>(); public Dictionary<string, TariffElement> TariffEntries { get; set; } = new Dictionary<string, TariffElement>();
/// <summary>
/// Well known language aware elements (AGB, tracking info, ...) to be displayed to user.
/// </summary>
public Dictionary<string, InfoElement> InfoEntries { get; set; } = new Dictionary<string, InfoElement>(); public Dictionary<string, InfoElement> InfoEntries { get; set; } = new Dictionary<string, InfoElement>();
} }
} }

View file

@ -438,5 +438,20 @@ namespace TINK.Model.Connector
name, name,
int.TryParse(bikeGroup?.bike_count ?? "0", out var countCity) ? countCity : 0, int.TryParse(bikeGroup?.bike_count ?? "0", out var countCity) ? countCity : 0,
bikeGroup?.bike_group ?? string.Empty); bikeGroup?.bike_group ?? string.Empty);
/// <summary>
/// Default value for reserve_timerange.
/// </summary>
private static int DEFAULTMAXRESERVATIONTIMESPAN = 15;
/// <summary>
/// Gets the reservation time span from response.
/// </summary>
/// <param name="description">Response to get time span from.</param>
/// <returns>Time span.</returns>
public static TimeSpan GetMaxReservationTimeSpan(this RentalDescription description) =>
TimeSpan.FromMinutes(int.TryParse(description?.reserve_timerange, out int minutes)
? minutes
: DEFAULTMAXRESERVATIONTIMESPAN );
} }
} }

View file

@ -313,7 +313,7 @@ namespace TINK.Model.Connector.Updater
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
// Contructor reported invalid arguemts (missing lock id, ....). // Constructor reported invalid arguments (missing lock id, ....).
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Booked bike with id {bikeInfo.bike} skipped. {ex.Message}"); Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Booked bike with id {bikeInfo.bike} skipped. {ex.Message}");
return null; return null;
} }

View file

@ -1,58 +1,56 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using TINK.Model.Bikes.BikeInfoNS;
namespace TINK.Model.Connector.Updater namespace TINK.Model.Connector.Updater
{ {
public static class RentalDescriptionFactory public static class RentalDescriptionFactory
{ {
/// <summary> /// <summary>
/// Creates rental description object from JSON- tarif description object. /// Creates rental description object from JSON- tariff description object.
/// </summary> /// </summary>
/// <param name="rentalDesciption">Source JSON object.</param> /// <param name="rentalDesciption">Source JSON object.</param>
/// <returns>Tariff description object.</returns> /// <returns>Tariff description object.</returns>
public static Bikes.BikeInfoNS.RentalDescription Create(this Repository.Response.RentalDescription rentalDesciption) public static RentalDescription Create(this Repository.Response.RentalDescription rentalDesciption)
{ {
Bikes.BikeInfoNS.RentalDescription.TariffElement CreateTarifEntry(string[] elementValue) RentalDescription.TariffElement CreateTarifEntry(string[] elementValue) =>
{ new RentalDescription.TariffElement
return new Bikes.BikeInfoNS.RentalDescription.TariffElement {
{ Description = elementValue != null && elementValue.Length > 0 ? elementValue[0] : string.Empty,
Description = elementValue != null && elementValue.Length > 0 ? elementValue[0] : string.Empty, Value = elementValue != null && elementValue.Length > 1 ? elementValue[1] : string.Empty,
Value = elementValue != null && elementValue.Length > 1 ? elementValue[1] : string.Empty, };
};
}
Bikes.BikeInfoNS.RentalDescription.InfoElement CreateInfoElement(string[] elementValue) RentalDescription.InfoElement CreateInfoElement(string[] elementValue) =>
{ new RentalDescription.InfoElement
return new Bikes.BikeInfoNS.RentalDescription.InfoElement
{ {
Key = elementValue != null && elementValue.Length > 0 ? elementValue[0] : string.Empty, Key = elementValue != null && elementValue.Length > 0 ? elementValue[0] : string.Empty,
Value = elementValue != null && elementValue.Length > 1 ? elementValue[1] : string.Empty, Value = elementValue != null && elementValue.Length > 1 ? elementValue[1] : string.Empty,
}; };
}
// Read tariff elements. // Read tariff elements.
var tarifEntries = rentalDesciption?.tarif_elements != null var tarifEntries = rentalDesciption?.tarif_elements != null
? rentalDesciption.tarif_elements.Select(x => new ? rentalDesciption.tarif_elements.Select(x => new
{ {
Key = x.Key, x.Key,
Value = CreateTarifEntry(x.Value) Value = CreateTarifEntry(x.Value)
}).ToLookup(x => x.Key, x => x.Value).ToDictionary(x => x.Key, x => x.First()) }).ToLookup(x => x.Key, x => x.Value).ToDictionary(x => x.Key, x => x.First())
: new Dictionary<string, Bikes.BikeInfoNS.RentalDescription.TariffElement>(); : new Dictionary<string, RentalDescription.TariffElement>();
// Read info elements. // Read info elements.
var InfoEntries = rentalDesciption?.rental_info != null var InfoEntries = rentalDesciption?.rental_info != null
? rentalDesciption.rental_info.Select(x => new ? rentalDesciption.rental_info.Select(x => new
{ {
Key = x.Key, x.Key,
Value = CreateInfoElement(x.Value) Value = CreateInfoElement(x.Value)
}).ToLookup(x => x.Key, x => x.Value).ToDictionary(x => x.Key, x => x.First()) }).ToLookup(x => x.Key, x => x.Value).ToDictionary(x => x.Key, x => x.First())
: new Dictionary<string, Bikes.BikeInfoNS.RentalDescription.InfoElement>(); : new Dictionary<string, RentalDescription.InfoElement>();
var bike = new Bikes.BikeInfoNS.RentalDescription var bike = new RentalDescription
{ {
Name = rentalDesciption?.name ?? string.Empty, Name = rentalDesciption?.name ?? string.Empty,
Id = int.TryParse(rentalDesciption?.id ?? string.Empty, out int number) ? number : (int?)null, Id = int.TryParse(rentalDesciption?.id ?? string.Empty, out int number) ? number : (int?)null,
MaxReservationTimeSpan = rentalDesciption.GetMaxReservationTimeSpan(),
TariffEntries = tarifEntries, TariffEntries = tarifEntries,
InfoEntries = InfoEntries InfoEntries = InfoEntries
}; };

View file

@ -2,13 +2,11 @@ using System;
using TINK.Model.Bikes.BikeInfoNS.BC; using TINK.Model.Bikes.BikeInfoNS.BC;
using TINK.Model.State; using TINK.Model.State;
using TINK.Model.Stations; using TINK.Model.Stations;
using TINK.Model.Stations.StationNS.Operator;
using TINK.Model.User.Account; using TINK.Model.User.Account;
using TINK.Repository.Exception; using TINK.Repository.Exception;
using TINK.Repository.Response; using TINK.Repository.Response;
using TINK.Repository.Response.Stations; using TINK.Repository.Response.Stations;
using TINK.Services.CopriApi; using TINK.Services.CopriApi;
using Xamarin.Forms;
using IBikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.IBikeInfoMutable; using IBikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.IBikeInfoMutable;
namespace TINK.Model.Connector.Updater namespace TINK.Model.Connector.Updater
@ -135,6 +133,7 @@ namespace TINK.Model.Connector.Updater
bike.State.Load( bike.State.Load(
InUseStateEnum.Reserved, InUseStateEnum.Reserved,
bikeInfo.GetFrom(), bikeInfo.GetFrom(),
bikeInfo.rental_description.GetMaxReservationTimeSpan(),
mailAddress, mailAddress,
bikeInfo.timeCode, bikeInfo.timeCode,
notifyLevel); notifyLevel);
@ -144,9 +143,9 @@ namespace TINK.Model.Connector.Updater
bike.State.Load( bike.State.Load(
InUseStateEnum.Booked, InUseStateEnum.Booked,
bikeInfo.GetFrom(), bikeInfo.GetFrom(),
mailAddress, mailAddress: mailAddress,
bikeInfo.timeCode, code: bikeInfo.timeCode,
notifyLevel); notifyLevel: notifyLevel);
break; break;
default: default:

View file

@ -1,4 +1,4 @@
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace TINK.Model.State namespace TINK.Model.State
{ {
@ -12,7 +12,7 @@ namespace TINK.Model.State
[KnownType(typeof(StateOccupiedInfo))] [KnownType(typeof(StateOccupiedInfo))]
public abstract class BaseState public abstract class BaseState
{ {
/// <summary> Constructor for Json serialization. </summary> /// <summary> Constructor for JSON serialization. </summary>
/// <param name="value">State value.</param> /// <param name="value">State value.</param>
protected BaseState(InUseStateEnum value) { } protected BaseState(InUseStateEnum value) { }

View file

@ -1,4 +1,4 @@
using System; using System;
namespace TINK.Model.State namespace TINK.Model.State
{ {
@ -9,8 +9,16 @@ namespace TINK.Model.State
{ {
string MailAddress { get; } string MailAddress { get; }
/// <summary>
/// Date of request/ booking action.
/// </summary>
DateTime? From { get; } DateTime? From { get; }
/// <summary>
/// Time span for which a bike can be reserved.
/// </summary>
TimeSpan? MaxReservationTimeSpan { get; }
string Code { get; } string Code { get; }
} }
} }

View file

@ -1,4 +1,4 @@
using System; using System;
namespace TINK.Model.State namespace TINK.Model.State
{ {
@ -6,15 +6,17 @@ namespace TINK.Model.State
{ {
InUseStateEnum Value { get; } InUseStateEnum Value { get; }
/// <summary> Updates state from webserver. </summary> /// <summary> Updates state from web server. </summary>
/// <param name="state">State of the bike.</param> /// <param name="state">State of the bike.</param>
/// <param name="from">Date time when bike was reserved/ booked.</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="reservationTimeSpan">Time span for which a bike can be reserved.</param>
/// <param name="mailAddress">Mail address of the one which reserved/ booked.</param>
/// <param name="code">Booking code if bike is booked or reserved.</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> /// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
void Load( void Load(
InUseStateEnum state, InUseStateEnum state,
DateTime? from = null, DateTime? from = null,
TimeSpan? reservationTimeSpan = null,
string mailAddress = null, string mailAddress = null,
string code = null, string code = null,
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All); Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All);

View file

@ -52,20 +52,21 @@ namespace TINK.Model.State
/// Constructs a state info object when state is requested. /// Constructs a state info object when state is requested.
/// </summary> /// </summary>
/// <param name="requestedAt">Date time when bike was requested</param> /// <param name="requestedAt">Date time when bike was requested</param>
/// <param name="maxReservationTimeSpan">Time span for which a bike can be reserved.</param>
/// <param name="mailAddress">Mail address of user which requested bike.</param> /// <param name="mailAddress">Mail address of user which requested bike.</param>
/// <param name="code">Booking code.</param> /// <param name="code">Booking code.</param>
/// <param name="dateTimeNowProvider">Date time provider to calculate remaining time.</param> /// <param name="dateTimeNowProvider">Date time provider to calculate remaining time.</param>
public StateInfo( public StateInfo(
Func<DateTime> dateTimeNowProvider, Func<DateTime> dateTimeNowProvider,
DateTime requestedAt, DateTime requestedAt,
TimeSpan maxReservationTimeSpan,
string mailAddress, string mailAddress,
string code) string code)
{ {
// Todo: Handle p_oFrom == null here.
// Todo: Handle p_oDuration == null here.
_InUseState = new StateRequestedInfo( _InUseState = new StateRequestedInfo(
dateTimeNowProvider ?? (() => DateTime.Now), dateTimeNowProvider ?? (() => DateTime.Now),
requestedAt, requestedAt,
maxReservationTimeSpan,
mailAddress, mailAddress,
code); code);
} }
@ -81,9 +82,6 @@ namespace TINK.Model.State
string p_strMailAddress, string p_strMailAddress,
string p_strCode) string p_strCode)
{ {
// Todo: Handle p_oFrom == null here.
// Todo: Clearify question: What to do if code changes form one value to another? This should never happen.
// Todo: Clearify question: What to do if from time changes form one value to another? This should never happen.
_InUseState = new StateOccupiedInfo( _InUseState = new StateOccupiedInfo(
p_oBookedAt, p_oBookedAt,
p_strMailAddress, p_strMailAddress,
@ -114,7 +112,7 @@ namespace TINK.Model.State
} }
/// <summary> /// <summary>
/// Date of request/ bookeing action. /// Date of request/ booking action.
/// </summary> /// </summary>
public DateTime? From public DateTime? From
{ {
@ -125,6 +123,14 @@ namespace TINK.Model.State
} }
} }
/// <summary>
/// Time span for which a bike can be reserved.
/// </summary>
public TimeSpan? MaxReservationTimeSpan =>
_InUseState is StateRequestedInfo reserved
? reserved.MaxReservationTimeSpan
: (TimeSpan?)null;
/// <summary> /// <summary>
/// Mail address. /// Mail address.
/// </summary> /// </summary>
@ -152,7 +158,7 @@ namespace TINK.Model.State
/// <summary> /// <summary>
/// Tries update /// Tries update
/// </summary> /// </summary>
/// <returns>True if reservation span has not exeeded and state remains reserved, false otherwise.</returns> /// <returns>True if reservation span has not exceeded and state remains reserved, false otherwise.</returns>
/// <todo>Implement logging of time stamps.</todo> /// <todo>Implement logging of time stamps.</todo>
public bool GetIsStillReserved(out TimeSpan? p_oRemainingTime) public bool GetIsStillReserved(out TimeSpan? p_oRemainingTime)
{ {

View file

@ -4,7 +4,7 @@ namespace TINK.Model.State
{ {
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.Serialization; using System.Runtime.Serialization;
/// <summary> /// <summary>
/// Manges the state of a bike. /// Manges the state of a bike.
/// </summary> /// </summary>
@ -90,19 +90,18 @@ namespace TINK.Model.State
return new StateInfo(state != null ? state.Value == InUseStateEnum.FeedbackPending : false); return new StateInfo(state != null ? state.Value == InUseStateEnum.FeedbackPending : false);
case InUseStateEnum.Reserved: case InUseStateEnum.Reserved:
// Todo: Handle p_oFrom == null here.
// Todo: Handle p_oDuration == null here.
return new StateInfo( return new StateInfo(
dateTimeNowProvider, state.From.HasValue ? dateTimeNowProvider : (() => DateTime.MaxValue),
state.From.Value, state.From.HasValue ? state.From.Value : DateTime.MaxValue,
state.MaxReservationTimeSpan ?? StateRequestedInfo.UNKNOWNMAXRESERVATIONTIMESPAN,
state.MailAddress, state.MailAddress,
state.Code); state.Code);
case InUseStateEnum.Booked: case InUseStateEnum.Booked:
// Todo: Handle p_oFrom == null here. // Todo: Handle p_oFrom == null here.
// Todo: Clearify question: What to do if code changes form one value to another? This should never happen. // Todo: Clarify question: What to do if code changes form one value to another? This should never happen.
// Todo: Clearify question: What to do if from time changes form one value to another? This should never happen. // Todo: Clarify question: What to do if from time changes form one value to another? This should never happen.
return new StateInfo( return new StateInfo(
state.From.Value, state.From.Value,
state.MailAddress, state.MailAddress,
@ -139,7 +138,7 @@ namespace TINK.Model.State
var l_oStateRequested = value as StateRequestedInfo; var l_oStateRequested = value as StateRequestedInfo;
if (l_oStateRequested != null) if (l_oStateRequested != null)
{ {
_StateInfo = new StateInfo(_DateTimeNowProvider, l_oStateRequested.From, l_oStateRequested.MailAddress, l_oStateRequested.Code); _StateInfo = new StateInfo(_DateTimeNowProvider, l_oStateRequested.From, l_oStateRequested.MaxReservationTimeSpan, l_oStateRequested.MailAddress, l_oStateRequested.Code);
return; return;
} }
@ -158,7 +157,7 @@ namespace TINK.Model.State
/// <summary> /// <summary>
/// Checks and updates state if required. /// Checks and updates state if required.
/// </summary> /// </summary>
/// <returns>Value indicating wheter state has changed</returns> /// <returns>Value indicating whether state has changed</returns>
public void UpdateOnTimeElapsed() public void UpdateOnTimeElapsed()
{ {
switch (_StateInfo.Value) switch (_StateInfo.Value)
@ -172,7 +171,7 @@ namespace TINK.Model.State
// Check if maximum reserved time has elapsed. // Check if maximum reserved time has elapsed.
if (!_StateInfo.GetIsStillReserved(out _RemainingTime)) if (!_StateInfo.GetIsStillReserved(out _RemainingTime))
{ {
// Time has elapsed, switch state to disposable and notfiy client // Time has elapsed, switch state to disposable and notify client
_StateInfo = new StateInfo(); _StateInfo = new StateInfo();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
return; return;
@ -181,15 +180,17 @@ namespace TINK.Model.State
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RemainingTime))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RemainingTime)));
} }
/// <summary> Updates state from webserver. </summary> /// <summary> Updates state from web server. </summary>
/// <param name="state">State of the bike.</param> /// <param name="state">State of the bike.</param>
/// <param name="from">Date time when bike was reserved/ booked.</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="maxReservationTimeSpan">Time span for which a bike can be reserved.</param>
/// <param name="mailAddress">Mail address of the one which reserved/ booked.</param>
/// <param name="code">Booking code if bike is booked or reserved.</param> /// <param name="code">Booking code if bike is booked or reserved.</param>
/// <param name="supressNotifyPropertyChanged">Controls whether notify property changed events are fired or not.</param> /// <param name="supressNotifyPropertyChanged">Controls whether notify property changed events are fired or not.</param>
public void Load( public void Load(
InUseStateEnum state, InUseStateEnum state,
DateTime? from = null, DateTime? from = null,
TimeSpan? maxReservationTimeSpan = null,
string mailAddress = null, string mailAddress = null,
string code = null, string code = null,
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All) Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All)
@ -206,11 +207,10 @@ namespace TINK.Model.State
break; break;
case InUseStateEnum.Reserved: case InUseStateEnum.Reserved:
// Todo: Handle p_oFrom == null here.
// Todo: Handle p_oDuration == null here.
_StateInfo = new StateInfo( _StateInfo = new StateInfo(
_DateTimeNowProvider, from.HasValue ? _DateTimeNowProvider : (() => DateTime.MaxValue),
from.Value, from.HasValue ? from.Value : DateTime.MaxValue,
maxReservationTimeSpan.HasValue ? maxReservationTimeSpan.Value : StateRequestedInfo.UNKNOWNMAXRESERVATIONTIMESPAN,
mailAddress, mailAddress,
code); code);
@ -245,7 +245,7 @@ namespace TINK.Model.State
} }
/// <summary> /// <summary>
/// If bike is reserved time raimaining while bike stays reserved, null otherwise. /// If bike is reserved time remaining while bike stays reserved, null otherwise.
/// </summary> /// </summary>
public TimeSpan? RemainingTime public TimeSpan? RemainingTime
{ {
@ -261,7 +261,7 @@ namespace TINK.Model.State
if (_RemainingTime.HasValue == false) if (_RemainingTime.HasValue == false)
{ {
// Value was not yet querried. // Value was not yet queried.
// Do query before returning object. // Do query before returning object.
_StateInfo.GetIsStillReserved(out _RemainingTime); _StateInfo.GetIsStillReserved(out _RemainingTime);
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -10,51 +10,63 @@ namespace TINK.Model.State
[DataContract] [DataContract]
public sealed class StateRequestedInfo : BaseState, IBaseState, INotAvailableState public sealed class StateRequestedInfo : BaseState, IBaseState, INotAvailableState
{ {
// Maximum time while reserving request is kept. /// <summary>
public static readonly TimeSpan MaximumReserveTime = new TimeSpan(0, 15, 0); // 15 mins /// Holds the value if time span is not known.
/// </summary>
/// <remarks> Default value see <see cref="TextToTypeHelper.DEFAULTMAXRESERVATIONTIMESPAN"/>. </remarks>
public static TimeSpan UNKNOWNMAXRESERVATIONTIMESPAN = TimeSpan.Zero;
// Reference to date time provider. // Reference to date time provider.
private Func<DateTime> _DateTimeNowProvider; private readonly Func<DateTime> _DateTimeNowProvider;
/// <summary>
/// Time span for which a bike can be reserved. Default value is zero.
/// </summary>
public TimeSpan MaxReservationTimeSpan { get; private set; } = UNKNOWNMAXRESERVATIONTIMESPAN;
/// <summary> /// <summary>
/// Prevents an invalid instance to be created. /// Prevents an invalid instance to be created.
/// Used by serializer only. /// Used by serializer only.
/// </summary> /// </summary>
private StateRequestedInfo() : base(InUseStateEnum.Reserved) internal StateRequestedInfo() : base(InUseStateEnum.Reserved)
{ {
// Is called in context of JSON deserialization. // Is called in context of JSON deserialization.
_DateTimeNowProvider = () => DateTime.Now; _DateTimeNowProvider = () => DateTime.Now;
} }
/// <summary> /// <summary>
/// Reservation performed with other device/ before start of app. /// Reservation performed with other device/ before start of app.
/// Date time info when bike was reserved has been received from webserver. /// Date time info when bike was reserved has been received from web server.
/// </summary> /// </summary>
/// <param name="p_oRemainingTime">Time span which holds duration how long bike still will be reserved.</param> /// <param name="maxReservationTimeSpan">Time span for which a bike can be reserved.</param>
[JsonConstructor] [JsonConstructor]
private StateRequestedInfo( private StateRequestedInfo(
InUseStateEnum Value, InUseStateEnum Value,
DateTime From, DateTime From,
TimeSpan maxReservationTimeSpan,
string MailAddress, string MailAddress,
string Code) : this(() => DateTime.Now, From, MailAddress, Code) string Code) : this(() => DateTime.Now, From, maxReservationTimeSpan, MailAddress, Code)
{ {
} }
/// <summary> /// <summary>
/// Reservation performed with other device/ before start of app. /// Reservation performed with other device/ before start of app.
/// Date time info when bike was reserved has been received from webserver. /// Date time info when bike was reserved has been received from web server.
/// </summary> /// </summary>
/// <param name="dateTimeNowProvider"> /// <param name="dateTimeNowProvider">
/// Used to to provide current date time information for potential calls of <seealso cref="GetIsStillReserved"/>. /// Used to provide current date time information for potential calls of <seealso cref="GetIsStillReserved"/>.
/// Not used to calculate remaining time because this duration whould always be shorter as the one received from webserver. /// Not used to calculate remaining time because this duration would always be shorter as the one received from web server.
/// </param> /// </param>
/// <param name="maxReservationTimeSpan">Time span for which a bike can be reserved.</param>
public StateRequestedInfo( public StateRequestedInfo(
Func<DateTime> dateTimeNowProvider, Func<DateTime> dateTimeNowProvider,
DateTime from, DateTime from,
TimeSpan maxReservationTimeSpan,
string mailAddress, string mailAddress,
string code) : base(InUseStateEnum.Reserved) string code) : base(InUseStateEnum.Reserved)
{ {
_DateTimeNowProvider = dateTimeNowProvider ?? (() => DateTime.Now); _DateTimeNowProvider = dateTimeNowProvider ?? (() => DateTime.Now);
MaxReservationTimeSpan = maxReservationTimeSpan;
From = from; From = from;
MailAddress = mailAddress; MailAddress = mailAddress;
Code = code; Code = code;
@ -63,34 +75,28 @@ namespace TINK.Model.State
/// <summary> /// <summary>
/// Tries update /// Tries update
/// </summary> /// </summary>
/// <returns>True if reservation span has not exeeded and state remains reserved, false otherwise.</returns> /// <returns>True if reservation span has not exceeded and state remains reserved, false otherwise.</returns>
/// <todo>Implement logging of time stamps.</todo> /// <todo>Implement logging of time stamps.</todo>
public bool GetIsStillReserved(out TimeSpan? p_oRemainingTime) public bool GetIsStillReserved(out TimeSpan? remainingTime)
{ {
var l_oTimeReserved = _DateTimeNowProvider().Subtract(From); var timeReserved = _DateTimeNowProvider().Subtract(From);
if (l_oTimeReserved > MaximumReserveTime) if (timeReserved > MaxReservationTimeSpan)
{ {
// Reservation has elapsed. To not update remaining time. // Reservation has elapsed. Do not update remaining time.
p_oRemainingTime = null; remainingTime = null;
return false; return false;
} }
p_oRemainingTime = MaximumReserveTime - l_oTimeReserved; remainingTime = MaxReservationTimeSpan - timeReserved;
return true; return true;
} }
/// <summary> /// <summary>
/// State reserved. /// State reserved.
/// Setter exists only for serialization purposes.
/// </summary> /// </summary>
public override InUseStateEnum Value public override InUseStateEnum Value =>
{ InUseStateEnum.Reserved;
get
{
return InUseStateEnum.Reserved;
}
}
/// <summary> /// <summary>
/// Date time when bike was reserved. /// Date time when bike was reserved.

View file

@ -692,6 +692,11 @@ namespace TINK.Model
AppResources.ChangeLog_3_0_365_MK_SB, AppResources.ChangeLog_3_0_365_MK_SB,
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike } new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
}, },
{
new Version(3, 0, 366),
AppResources.ChangeLog_3_0_366_MK_SB,
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
},
}; };
/// <summary> Manges the whats new information.</summary> /// <summary> Manges the whats new information.</summary>

View file

@ -802,6 +802,15 @@ namespace TINK.MultilingualResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to On the &quot;Select bike&quot; page you can now search and rent several bikes one after the other. Afterwards you will find them under &quot;My bikes&quot;..
/// </summary>
public static string ChangeLog_3_0_366_MK_SB {
get {
return ResourceManager.GetString("ChangeLog_3_0_366_MK_SB", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to We have fixed some bugs. Enjoy the ride!. /// Looks up a localized string similar to We have fixed some bugs. Enjoy the ride!.
/// </summary> /// </summary>
@ -2103,7 +2112,16 @@ namespace TINK.MultilingualResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Bike ID including prefix. /// Looks up a localized string similar to Search.
/// </summary>
public static string MarkingFindBikeButton {
get {
return ResourceManager.GetString("MarkingFindBikeButton", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bike id.
/// </summary> /// </summary>
public static string MarkingFindBikeLabel { public static string MarkingFindBikeLabel {
get { get {
@ -2111,6 +2129,15 @@ namespace TINK.MultilingualResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to You search a .
/// </summary>
public static string MarkingFindBikeTypeOfBikeText {
get {
return ResourceManager.GetString("MarkingFindBikeTypeOfBikeText", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Menu. /// Looks up a localized string similar to Menu.
/// </summary> /// </summary>
@ -2662,6 +2689,24 @@ namespace TINK.MultilingualResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to You can search only the bikes of the selected bike type. Change your selection on the &quot;Bike locations&quot; page..
/// </summary>
public static string MessageBikeTypeInfoText {
get {
return ResourceManager.GetString("MessageBikeTypeInfoText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Selected bike type.
/// </summary>
public static string MessageBikeTypeInfoTitle {
get {
return ResourceManager.GetString("MessageBikeTypeInfoTitle", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Connection error when canceling the reservation!. /// Looks up a localized string similar to Connection error when canceling the reservation!.
/// </summary> /// </summary>
@ -3039,7 +3084,7 @@ namespace TINK.MultilingualResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Bike ID including prefix e.g. TR15. /// Looks up a localized string similar to Prefix and No., e.g. TR15.
/// </summary> /// </summary>
public static string PlaceholderFindBike { public static string PlaceholderFindBike {
get { get {
@ -3248,7 +3293,7 @@ namespace TINK.MultilingualResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Code {0}, location {1}, max. reservation time of {2} min. expired.. /// Looks up a localized string similar to Code {0}, location {1}, max. reservation time expired..
/// </summary> /// </summary>
public static string StatusTextReservationExpiredCodeLocationMaxReservationTime { public static string StatusTextReservationExpiredCodeLocationMaxReservationTime {
get { get {
@ -3266,7 +3311,7 @@ namespace TINK.MultilingualResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Code {0}, max. reservation time of {1} min. expired.. /// Looks up a localized string similar to Code {0}, max. reservation time expired..
/// </summary> /// </summary>
public static string StatusTextReservationExpiredCodeMaxReservationTime { public static string StatusTextReservationExpiredCodeMaxReservationTime {
get { get {
@ -3284,7 +3329,7 @@ namespace TINK.MultilingualResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Location {0}, max. reservation time of {1} min. expired.. /// Looks up a localized string similar to Location {0}, max. reservation time expired..
/// </summary> /// </summary>
public static string StatusTextReservationExpiredLocationMaxReservationTime { public static string StatusTextReservationExpiredLocationMaxReservationTime {
get { get {
@ -3302,7 +3347,7 @@ namespace TINK.MultilingualResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Max. reservation time of {0} min. expired.. /// Looks up a localized string similar to Max. reservation time expired..
/// </summary> /// </summary>
public static string StatusTextReservationExpiredMaximumReservationTime { public static string StatusTextReservationExpiredMaximumReservationTime {
get { get {

View file

@ -182,7 +182,7 @@ Die Protokolldatei enthält Ihre App-Nutzungsdaten sowie Systeminformationen. Di
Bitte melden Sie sich erneut an.</value> Bitte melden Sie sich erneut an.</value>
</data> </data>
<data name="StatusTextReservationExpiredCodeMaxReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeMaxReservationTime" xml:space="preserve">
<value>Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen. <value>Code ist {0}, max. Reservierungszeit abgelaufen.
</value> </value>
</data> </data>
<data name="StatusTextReservationExpiredCodeRemaining" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeRemaining" xml:space="preserve">
@ -199,13 +199,13 @@ Bitte melden Sie sich erneut an.</value>
<value>Noch {0} Min. reserviert.</value> <value>Noch {0} Min. reserviert.</value>
</data> </data>
<data name="StatusTextReservationExpiredMaximumReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredMaximumReservationTime" xml:space="preserve">
<value>Max. Reservierungszeit von {0} Min. abgelaufen.</value> <value>Max. Reservierungszeit abgelaufen.</value>
</data> </data>
<data name="StatusTextBookedCodeSince" xml:space="preserve"> <data name="StatusTextBookedCodeSince" xml:space="preserve">
<value>Code ist {0}, gemietet seit {1}.</value> <value>Code ist {0}, gemietet seit {1}.</value>
</data> </data>
<data name="StatusTextReservationExpiredLocationMaxReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredLocationMaxReservationTime" xml:space="preserve">
<value>Standort {0}, max. Reservierungszeit von {1} Min. abgelaufen. <value>Standort {0}, max. Reservierungszeit abgelaufen.
</value> </value>
</data> </data>
<data name="StatusTextAvailable" xml:space="preserve"> <data name="StatusTextAvailable" xml:space="preserve">
@ -216,7 +216,7 @@ Bitte melden Sie sich erneut an.</value>
</value> </value>
</data> </data>
<data name="StatusTextReservationExpiredCodeLocationMaxReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeLocationMaxReservationTime" xml:space="preserve">
<value>Code ist {0}, Standort {1}, max. Reservierungszeit von {2} Min. abgelaufen. <value>Code ist {0}, Standort {1}, max. Reservierungszeit abgelaufen.
</value> </value>
</data> </data>
<data name="StatusTextReservationExpiredCodeLocationReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeLocationReservationTime" xml:space="preserve">
@ -1007,7 +1007,7 @@ Außerdem: Kleine Grafiken lassen auf einen Blick erkennen um was für einen Rad
<value>Einwilligung</value> <value>Einwilligung</value>
</data> </data>
<data name="PlaceholderFindBike" xml:space="preserve"> <data name="PlaceholderFindBike" xml:space="preserve">
<value>Rad-ID inklusive Präfix, z.B. TR15</value> <value>Präfix und Nr., z.B. TR15</value>
</data> </data>
<data name="ChangeLog3_0_339_MK" xml:space="preserve"> <data name="ChangeLog3_0_339_MK" xml:space="preserve">
<value>Die Lastenräder aus den Vororten zeigen nun ihre Heimatstation im Namen an. Diese Räder müssen dort wieder abgeben werden! <value>Die Lastenräder aus den Vororten zeigen nun ihre Heimatstation im Namen an. Diese Räder müssen dort wieder abgeben werden!
@ -1171,7 +1171,7 @@ Außerdem:&lt;br/&gt;
- Paketaktualisierungen</value> - Paketaktualisierungen</value>
</data> </data>
<data name="MarkingFindBikeLabel" xml:space="preserve"> <data name="MarkingFindBikeLabel" xml:space="preserve">
<value>Rad-ID inklusive Präfix</value> <value>Rad-ID</value>
</data> </data>
<data name="MarkingSearchBike" xml:space="preserve"> <data name="MarkingSearchBike" xml:space="preserve">
<value>Rad suchen</value> <value>Rad suchen</value>
@ -1188,4 +1188,19 @@ Außerdem:&lt;br/&gt;
<data name="ChangeLog_3_0_365_MK_SB" xml:space="preserve"> <data name="ChangeLog_3_0_365_MK_SB" xml:space="preserve">
<value>Kleine Verbesserungen in Design und Performance.</value> <value>Kleine Verbesserungen in Design und Performance.</value>
</data> </data>
<data name="MarkingFindBikeTypeOfBikeText" xml:space="preserve">
<value>Sie suchen ein </value>
</data>
<data name="MessageBikeTypeInfoText" xml:space="preserve">
<value>Sie können nur die Fahrräder des ausgewählten Fahrradtyps suchen. Ändern Sie Ihre Auswahl auf der Seite "Radstandorte".</value>
</data>
<data name="MessageBikeTypeInfoTitle" xml:space="preserve">
<value>Ausgewählter Fahrradtyp</value>
</data>
<data name="MarkingFindBikeButton" xml:space="preserve">
<value>Suchen</value>
</data>
<data name="ChangeLog_3_0_366_MK_SB" xml:space="preserve">
<value>Auf der Seite "Rad auswählen" können Sie nun nacheinander mehrere Räder suchen und mieten. Im Anschluss finden Sie diese unter "Meine Räder".</value>
</data>
</root> </root>

View file

@ -299,7 +299,7 @@ The log file contains your app usage data as well as system information. The dat
<value>Rented since {0}.</value> <value>Rented since {0}.</value>
</data> </data>
<data name="StatusTextReservationExpiredMaximumReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredMaximumReservationTime" xml:space="preserve">
<value>Max. reservation time of {0} min. expired.</value> <value>Max. reservation time expired.</value>
</data> </data>
<data name="StatusTextReservationExpiredCodeRemaining" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeRemaining" xml:space="preserve">
<value>Code {0}, still {1} min. reserved.</value> <value>Code {0}, still {1} min. reserved.</value>
@ -308,7 +308,7 @@ The log file contains your app usage data as well as system information. The dat
<value>Still {0} min. reserved.</value> <value>Still {0} min. reserved.</value>
</data> </data>
<data name="StatusTextReservationExpiredCodeMaxReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeMaxReservationTime" xml:space="preserve">
<value>Code {0}, max. reservation time of {1} min. expired.</value> <value>Code {0}, max. reservation time expired.</value>
</data> </data>
<data name="StatusTextAvailable" xml:space="preserve"> <data name="StatusTextAvailable" xml:space="preserve">
<value>Available.</value> <value>Available.</value>
@ -317,13 +317,13 @@ The log file contains your app usage data as well as system information. The dat
<value>Code {0}, location {1}, rented since {2}.</value> <value>Code {0}, location {1}, rented since {2}.</value>
</data> </data>
<data name="StatusTextReservationExpiredCodeLocationMaxReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeLocationMaxReservationTime" xml:space="preserve">
<value>Code {0}, location {1}, max. reservation time of {2} min. expired.</value> <value>Code {0}, location {1}, max. reservation time expired.</value>
</data> </data>
<data name="StatusTextReservationExpiredCodeLocationReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredCodeLocationReservationTime" xml:space="preserve">
<value>Code {0}, location {1}, still {2} min. reserved.</value> <value>Code {0}, location {1}, still {2} min. reserved.</value>
</data> </data>
<data name="StatusTextReservationExpiredLocationMaxReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredLocationMaxReservationTime" xml:space="preserve">
<value>Location {0}, max. reservation time of {1} min. expired.</value> <value>Location {0}, max. reservation time expired.</value>
</data> </data>
<data name="StatusTextReservationExpiredLocationReservationTime" xml:space="preserve"> <data name="StatusTextReservationExpiredLocationReservationTime" xml:space="preserve">
<value>Location {0}, still {1} min. reserved.</value> <value>Location {0}, still {1} min. reserved.</value>
@ -1112,7 +1112,7 @@ In addition: Small graphics let you see at a glance what type of bike it is.</va
<value>Consent</value> <value>Consent</value>
</data> </data>
<data name="PlaceholderFindBike" xml:space="preserve"> <data name="PlaceholderFindBike" xml:space="preserve">
<value>Bike ID including prefix e.g. TR15</value> <value>Prefix and No., e.g. TR15</value>
</data> </data>
<data name="ChangeLog3_0_339_MK" xml:space="preserve"> <data name="ChangeLog3_0_339_MK" xml:space="preserve">
<value>The cargo bikes from the suburbs now show their home station in their name. These bikes must be returned there! <value>The cargo bikes from the suburbs now show their home station in their name. These bikes must be returned there!
@ -1260,7 +1260,7 @@ Also:&lt;br/&gt;
- Package updates</value> - Package updates</value>
</data> </data>
<data name="MarkingFindBikeLabel" xml:space="preserve"> <data name="MarkingFindBikeLabel" xml:space="preserve">
<value>Bike ID including prefix</value> <value>Bike id</value>
</data> </data>
<data name="MarkingSearchBike" xml:space="preserve"> <data name="MarkingSearchBike" xml:space="preserve">
<value>Search bike</value> <value>Search bike</value>
@ -1277,4 +1277,19 @@ Also:&lt;br/&gt;
<data name="ChangeLog_3_0_365_MK_SB" xml:space="preserve"> <data name="ChangeLog_3_0_365_MK_SB" xml:space="preserve">
<value>Minor design and performance improvements.</value> <value>Minor design and performance improvements.</value>
</data> </data>
<data name="MarkingFindBikeTypeOfBikeText" xml:space="preserve">
<value>You search a </value>
</data>
<data name="MessageBikeTypeInfoText" xml:space="preserve">
<value>You can search only the bikes of the selected bike type. Change your selection on the "Bike locations" page.</value>
</data>
<data name="MessageBikeTypeInfoTitle" xml:space="preserve">
<value>Selected bike type</value>
</data>
<data name="MarkingFindBikeButton" xml:space="preserve">
<value>Search</value>
</data>
<data name="ChangeLog_3_0_366_MK_SB" xml:space="preserve">
<value>On the "Select bike" page you can now search and rent several bikes one after the other. Afterwards you will find them under "My bikes".</value>
</data>
</root> </root>

View file

@ -237,8 +237,8 @@ Please log in again.</source>
Bitte melden Sie sich erneut an.</target> Bitte melden Sie sich erneut an.</target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextReservationExpiredCodeMaxReservationTime" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextReservationExpiredCodeMaxReservationTime" translate="yes" xml:space="preserve">
<source>Code {0}, max. reservation time of {1} min. expired.</source> <source>Code {0}, max. reservation time expired.</source>
<target state="translated">Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen. <target state="translated">Code ist {0}, max. Reservierungszeit abgelaufen.
</target> </target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextReservationExpiredCodeRemaining" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextReservationExpiredCodeRemaining" translate="yes" xml:space="preserve">
@ -259,16 +259,16 @@ Bitte melden Sie sich erneut an.</target>
<target state="translated">Noch {0} Min. reserviert.</target> <target state="translated">Noch {0} Min. reserviert.</target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextReservationExpiredMaximumReservationTime" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextReservationExpiredMaximumReservationTime" translate="yes" xml:space="preserve">
<source>Max. reservation time of {0} min. expired.</source> <source>Max. reservation time expired.</source>
<target state="translated">Max. Reservierungszeit von {0} Min. abgelaufen.</target> <target state="translated">Max. Reservierungszeit abgelaufen.</target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextBookedCodeSince" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextBookedCodeSince" translate="yes" xml:space="preserve">
<source>Code {0}, rented since {1}.</source> <source>Code {0}, rented since {1}.</source>
<target state="translated">Code ist {0}, gemietet seit {1}.</target> <target state="translated">Code ist {0}, gemietet seit {1}.</target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextReservationExpiredLocationMaxReservationTime" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextReservationExpiredLocationMaxReservationTime" translate="yes" xml:space="preserve">
<source>Location {0}, max. reservation time of {1} min. expired.</source> <source>Location {0}, max. reservation time expired.</source>
<target state="translated">Standort {0}, max. Reservierungszeit von {1} Min. abgelaufen. <target state="translated">Standort {0}, max. Reservierungszeit abgelaufen.
</target> </target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextAvailable" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextAvailable" translate="yes" xml:space="preserve">
@ -281,8 +281,8 @@ Bitte melden Sie sich erneut an.</target>
</target> </target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextReservationExpiredCodeLocationMaxReservationTime" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextReservationExpiredCodeLocationMaxReservationTime" translate="yes" xml:space="preserve">
<source>Code {0}, location {1}, max. reservation time of {2} min. expired.</source> <source>Code {0}, location {1}, max. reservation time expired.</source>
<target state="translated">Code ist {0}, Standort {1}, max. Reservierungszeit von {2} Min. abgelaufen. <target state="translated">Code ist {0}, Standort {1}, max. Reservierungszeit abgelaufen.
</target> </target>
</trans-unit> </trans-unit>
<trans-unit id="StatusTextReservationExpiredCodeLocationReservationTime" translate="yes" xml:space="preserve"> <trans-unit id="StatusTextReservationExpiredCodeLocationReservationTime" translate="yes" xml:space="preserve">
@ -1373,8 +1373,8 @@ Außerdem: Kleine Grafiken lassen auf einen Blick erkennen um was für einen Rad
<target state="translated">Einwilligung</target> <target state="translated">Einwilligung</target>
</trans-unit> </trans-unit>
<trans-unit id="PlaceholderFindBike" translate="yes" xml:space="preserve"> <trans-unit id="PlaceholderFindBike" translate="yes" xml:space="preserve">
<source>Bike ID including prefix e.g. TR15</source> <source>Prefix and No., e.g. TR15</source>
<target state="translated">Rad-ID inklusive Präfix, z.B. TR15</target> <target state="translated">Präfix und Nr., z.B. TR15</target>
</trans-unit> </trans-unit>
<trans-unit id="ChangeLog3_0_339_MK" translate="yes" xml:space="preserve"> <trans-unit id="ChangeLog3_0_339_MK" translate="yes" xml:space="preserve">
<source>The cargo bikes from the suburbs now show their home station in their name. These bikes must be returned there! <source>The cargo bikes from the suburbs now show their home station in their name. These bikes must be returned there!
@ -1620,8 +1620,8 @@ Außerdem:&lt;br/&gt;
- Paketaktualisierungen</target> - Paketaktualisierungen</target>
</trans-unit> </trans-unit>
<trans-unit id="MarkingFindBikeLabel" translate="yes" xml:space="preserve"> <trans-unit id="MarkingFindBikeLabel" translate="yes" xml:space="preserve">
<source>Bike ID including prefix</source> <source>Bike id</source>
<target state="translated">Rad-ID inklusive Präfix</target> <target state="translated">Rad-ID</target>
</trans-unit> </trans-unit>
<trans-unit id="MarkingSearchBike" translate="yes" xml:space="preserve"> <trans-unit id="MarkingSearchBike" translate="yes" xml:space="preserve">
<source>Search bike</source> <source>Search bike</source>
@ -1643,6 +1643,26 @@ Außerdem:&lt;br/&gt;
<source>Minor design and performance improvements.</source> <source>Minor design and performance improvements.</source>
<target state="translated">Kleine Verbesserungen in Design und Performance.</target> <target state="translated">Kleine Verbesserungen in Design und Performance.</target>
</trans-unit> </trans-unit>
<trans-unit id="MarkingFindBikeTypeOfBikeText" translate="yes" xml:space="preserve">
<source>You search a </source>
<target state="translated">Sie suchen ein </target>
</trans-unit>
<trans-unit id="MessageBikeTypeInfoText" translate="yes" xml:space="preserve">
<source>You can search only the bikes of the selected bike type. Change your selection on the "Bike locations" page.</source>
<target state="translated">Sie können nur die Fahrräder des ausgewählten Fahrradtyps suchen. Ändern Sie Ihre Auswahl auf der Seite "Radstandorte".</target>
</trans-unit>
<trans-unit id="MessageBikeTypeInfoTitle" translate="yes" xml:space="preserve">
<source>Selected bike type</source>
<target state="translated">Ausgewählter Fahrradtyp</target>
</trans-unit>
<trans-unit id="MarkingFindBikeButton" translate="yes" xml:space="preserve">
<source>Search</source>
<target state="translated">Suchen</target>
</trans-unit>
<trans-unit id="ChangeLog_3_0_366_MK_SB" translate="yes" xml:space="preserve">
<source>On the "Select bike" page you can now search and rent several bikes one after the other. Afterwards you will find them under "My bikes".</source>
<target state="translated">Auf der Seite "Rad auswählen" können Sie nun nacheinander mehrere Räder suchen und mieten. Im Anschluss finden Sie diese unter "Meine Räder".</target>
</trans-unit>
</group> </group>
</body> </body>
</file> </file>

View file

@ -550,7 +550,7 @@ namespace TINK.Repository
#endif #endif
} }
/// <summary> Gets canel booking request response.</summary> /// <summary> Gets cancel booking request response.</summary>
/// <param name="copriHost">Host to connect to. </param> /// <param name="copriHost">Host to connect to. </param>
/// <param name="command">Command to log user in.</param> /// <param name="command">Command to log user in.</param>
/// <returns>Response on cancel booking request.</returns> /// <returns>Response on cancel booking request.</returns>

View file

@ -1376,7 +1376,7 @@ namespace TINK.Repository
} }
/// <summary> /// <summary>
/// Gets canel booking request response. /// Gets cancel booking request response.
/// </summary> /// </summary>
/// <param name="bikeId">Id of the bike to book.</param> /// <param name="bikeId">Id of the bike to book.</param>
/// <param name="cookie">Cookie of the logged in user.</param> /// <param name="cookie">Cookie of the logged in user.</param>
@ -1613,7 +1613,7 @@ namespace TINK.Repository
} }
/// <summary> /// <summary>
/// Gets canel booking request response. /// Gets cancel booking request response.
/// </summary> /// </summary>
/// <param name="bikeId">Id of the bike to book.</param> /// <param name="bikeId">Id of the bike to book.</param>
/// <param name="cookie">Cookie of the logged in user.</param> /// <param name="cookie">Cookie of the logged in user.</param>

View file

@ -1,9 +1,9 @@
namespace TINK.Repository.Exception namespace TINK.Repository.Exception
{ {
/// <summary> /// <summary>
/// Is fired with reqest used a cookie which is not defined. /// Is fired with request used a cookie which is not defined.
/// Reasons for cookie to be not defined might be /// Reasons for cookie to be not defined might be
/// - user used more thant 8 different devices (copri invalidates cookies in this case) /// - user used more that 8 different devices (copri invalidates cookies in this case)
/// - user account has been deleted? /// - user account has been deleted?
/// </summary> /// </summary>
public class AuthcookieNotDefinedException : InvalidResponseException<Response.ResponseBase> public class AuthcookieNotDefinedException : InvalidResponseException<Response.ResponseBase>
@ -19,7 +19,7 @@
/// Gets whether authcookie is defined or not. /// Gets whether authcookie is defined or not.
/// </summary> /// </summary>
/// <param name="reponse">Response to check</param> /// <param name="reponse">Response to check</param>
/// <param name="actionText">Text holding contectin in which authcookie is checked.</param> /// <param name="actionText">Text holding context in which authcookie is checked.</param>
/// <param name="exception">Exception thrown if cookie is not defined.</param> /// <param name="exception">Exception thrown if cookie is not defined.</param>
/// <returns></returns> /// <returns></returns>
public static bool IsAuthcookieNotDefined( public static bool IsAuthcookieNotDefined(
@ -29,7 +29,7 @@
{ {
if (reponse == null || reponse.response_state == null) if (reponse == null || reponse.response_state == null)
{ {
// Empty response or response withoud response state is no authcookie not defined exeception. // Empty response or response without response state is no authcookie not defined exception.
exception = null; exception = null;
return false; return false;
} }

View file

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace TINK.Repository.Response namespace TINK.Repository.Response
@ -22,6 +22,12 @@ namespace TINK.Repository.Response
[DataMember] [DataMember]
public string id { get; private set; } public string id { get; private set; }
/// <summary>
/// Holds the time span in minutes for which a bike can be reserved.
/// </summary>
[DataMember]
public string reserve_timerange { get; private set; }
/// <summary> Holds tariff entires to show to user.</summary> /// <summary> Holds tariff entires to show to user.</summary>
[DataMember] [DataMember]
public Dictionary< public Dictionary<

View file

@ -115,7 +115,7 @@ namespace TINK.Services.CopriApi
throw new BikeStillInStationException("Booking was canceled because bike is still in station."); throw new BikeStillInStationException("Booking was canceled because bike is still in station.");
} }
// Upate booking state. // Update booking state.
bike.Load( bike.Load(
response, response,
mailAddress, mailAddress,

View file

@ -95,7 +95,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC
} }
} }
/// <summary> Gets visiblity of the copri command button. </summary> /// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible public bool IsButtonVisible
=> RequestHandler.IsButtonVisible => RequestHandler.IsButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ; && Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;

View file

@ -1,4 +1,4 @@
using System; using System;
using TINK.Model.Connector; using TINK.Model.Connector;
using TINK.Model.Device; using TINK.Model.Device;
using TINK.Model.User; using TINK.Model.User;
@ -62,12 +62,12 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
} }
} }
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary> /// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired { get; set; } public bool IsRemoveBikeRequired { get; set; }
/// <summary> /// <summary>
/// Constructs the reqest handler base. /// Constructs the request handler base.
/// </summary> /// </summary>
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param> /// <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="smartDevice">Provides info about the smart device (phone, tablet, ...).</param>

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Serilog; using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BC; using TINK.Model.Bikes.BikeInfoNS.BC;
@ -55,7 +55,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
/// <summary>Gets the is connected state. </summary> /// <summary>Gets the is connected state. </summary>
public bool IsConnected { get; set; } public bool IsConnected { get; set; }
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary> /// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false; public bool IsRemoveBikeRequired => false;
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param> /// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>

View file

@ -37,7 +37,10 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
var l_oResult = await ViewService.DisplayAlert( var l_oResult = await ViewService.DisplayAlert(
string.Empty, string.Empty,
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes), string.Format(
AppResources.QuestionReserveBike,
SelectedBike.GetFullDisplayName(),
SelectedBike.TariffDescription?.MaxReservationTimeSpan.TotalMinutes ?? 0),
AppResources.MessageAnswerYes, AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo); AppResources.MessageAnswerNo);

View file

@ -177,14 +177,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
RaisePropertyChangedEvent(lastHandler); RaisePropertyChangedEvent(lastHandler);
} }
/// <summary> Gets visiblity of the copri command button. </summary> /// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible public bool IsButtonVisible
=> RequestHandler.IsButtonVisible; => RequestHandler.IsButtonVisible;
/// <summary> Gets the text of the copri command button. </summary> /// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText; public string ButtonText => RequestHandler.ButtonText;
/// <summary> Gets visiblity of the ILockIt command button. </summary> /// <summary> Gets visibility of the ILockIt command button. </summary>
public bool IsLockitButtonVisible public bool IsLockitButtonVisible
=> RequestHandler.IsLockitButtonVisible; => RequestHandler.IsLockitButtonVisible;

View file

@ -11,7 +11,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable> public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable>
{ {
/// <summary> /// <summary>
/// Constructs the reqest handler base. /// Constructs the request handler base.
/// </summary> /// </summary>
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param> /// <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="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>

View file

@ -62,7 +62,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Ask whether to really reserve bike? // Ask whether to really reserve bike?
var alertResult = await ViewService.DisplayAlert( var alertResult = await ViewService.DisplayAlert(
string.Empty, string.Empty,
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes), string.Format(
AppResources.QuestionReserveBike,
SelectedBike.GetFullDisplayName(),
SelectedBike.TariffDescription?.MaxReservationTimeSpan.TotalMinutes ?? 0),
AppResources.MessageAnswerYes, AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo); AppResources.MessageAnswerNo);

View file

@ -1,10 +1,10 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
{ {
public interface IRequestHandler : IRequestHandlerBase public interface IRequestHandler : IRequestHandlerBase
{ {
/// <summary> Gets a value indicating whether the ILockIt button which is managed by request hadnler is visible or not. </summary> /// <summary> Gets a value indicating whether the ILockIt button which is managed by request handler is visible or not. </summary>
bool IsLockitButtonVisible { get; } bool IsLockitButtonVisible { get; }
/// <summary> Gets the text of the ILockIt button which is managed by request handler. </summary> /// <summary> Gets the text of the ILockIt button which is managed by request handler. </summary>
@ -13,13 +13,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
/// <summary> /// <summary>
/// Performs the copri action to be executed when user presses the copri button managed by request handler. /// Performs the copri action to be executed when user presses the copri button managed by request handler.
/// </summary> /// </summary>
/// <returns>New handler object if action suceeded, same handler otherwise.</returns> /// <returns>New handler object if action succeeded, same handler otherwise.</returns>
Task<IRequestHandler> HandleRequestOption1(); Task<IRequestHandler> HandleRequestOption1();
Task<IRequestHandler> HandleRequestOption2(); Task<IRequestHandler> HandleRequestOption2();
/// <summary> /// <summary>
/// Holds error discription (invalid state). /// Holds error description (invalid state).
/// </summary> /// </summary>
string ErrorText { get; } string ErrorText { get; }
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Serilog; using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
@ -36,7 +36,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
public string ButtonText => GetType().Name; public string ButtonText => GetType().Name;
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary> /// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false; public bool IsRemoveBikeRequired => false;
public string ErrorText { get; } public string ErrorText { get; }

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Serilog; using Serilog;
using TINK.Model.State; using TINK.Model.State;
@ -38,7 +38,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
public bool IsConnected => throw new NotImplementedException(); public bool IsConnected => throw new NotImplementedException();
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary> /// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false; public bool IsRemoveBikeRequired => false;
public async Task<IRequestHandler> HandleRequestOption1() public async Task<IRequestHandler> HandleRequestOption1()

View file

@ -136,7 +136,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
RaisePropertyChangedEvent(lastHandler); RaisePropertyChangedEvent(lastHandler);
} }
/// <summary> Gets visiblity of the copri command button. </summary> /// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible public bool IsButtonVisible
=> RequestHandler.IsButtonVisible => RequestHandler.IsButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ; && Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;
@ -144,7 +144,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
/// <summary> Gets the text of the copri command button. </summary> /// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText; public string ButtonText => RequestHandler.ButtonText;
/// <summary> Gets visiblity of the ILockIt command button. </summary> /// <summary> Gets visibility of the ILockIt command button. </summary>
public bool IsLockitButtonVisible public bool IsLockitButtonVisible
=> RequestHandler.IsLockitButtonVisible => RequestHandler.IsLockitButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ; && Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;

View file

@ -1,4 +1,4 @@
using System; using System;
using TINK.Model.Connector; using TINK.Model.Connector;
using TINK.Model.Device; using TINK.Model.Device;
using TINK.Model.User; using TINK.Model.User;
@ -9,7 +9,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable> public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable>
{ {
/// <summary> /// <summary>
/// Constructs the reqest handler base. /// Constructs the request handler base.
/// </summary> /// </summary>
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param> /// <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="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>

View file

@ -132,7 +132,10 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Ask whether to really reserve bike? // Ask whether to really reserve bike?
var alertResult = await ViewService.DisplayAlert( var alertResult = await ViewService.DisplayAlert(
string.Empty, string.Empty,
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes), string.Format(
AppResources.QuestionReserveBike,
SelectedBike.GetFullDisplayName(),
SelectedBike.TariffDescription?.MaxReservationTimeSpan.TotalMinutes ?? 0),
AppResources.MessageAnswerYes, AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo); AppResources.MessageAnswerNo);

View file

@ -1,4 +1,4 @@
using System; using System;
using Serilog; using Serilog;
using TINK.Model.Bikes.BikeInfoNS.CopriLock; using TINK.Model.Bikes.BikeInfoNS.CopriLock;
using TINK.Model.Connector; using TINK.Model.Connector;
@ -54,7 +54,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
case Model.State.InUseStateEnum.Disposable: case Model.State.InUseStateEnum.Disposable:
// Bike is reserved, selecte action depending on lock state. // Bike is reserved, selected action depending on lock state.
return new DisposableClosed( return new DisposableClosed(
selectedCopriLock, selectedCopriLock,
isConnectedDelegate, isConnectedDelegate,
@ -79,7 +79,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
case Model.State.InUseStateEnum.Booked: case Model.State.InUseStateEnum.Booked:
// Bike is booked, selecte action depending on lock state. // Bike is booked, selected action depending on lock state.
var lockState = selectedCopriLock.LockInfo.State; var lockState = selectedCopriLock.LockInfo.State;
switch (lockState) switch (lockState)
{ {

View file

@ -1,4 +1,4 @@
namespace TINK.ViewModel.Bikes.Bike namespace TINK.ViewModel.Bikes.Bike
{ {
/// <summary> /// <summary>
/// Base interface for Copri and ILockIt request handler. /// Base interface for Copri and ILockIt request handler.
@ -22,7 +22,7 @@
/// <summary>Gets the is connected state. </summary> /// <summary>Gets the is connected state. </summary>
bool IsConnected { get; } bool IsConnected { get; }
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary> /// <summary> Gets if the bike has to be removed after action has been completed. </summary>
bool IsRemoveBikeRequired { get; } bool IsRemoveBikeRequired { get; }
} }
} }

View file

@ -13,7 +13,7 @@ namespace TINK.ViewModel.Bikes.Bike
private const string RIDETYPEKEY = "AAFAHRTEN"; private const string RIDETYPEKEY = "AAFAHRTEN";
public TariffDescriptionViewModel(RentalDescription tariff) public TariffDescriptionViewModel(IRentalDescription tariff)
{ {
Name = tariff?.Name ?? string.Empty; Name = tariff?.Name ?? string.Empty;

View file

@ -1,5 +1,4 @@
using System; using System;
using TINK.Model.State;
using TINK.MultilingualResources; using TINK.MultilingualResources;
using TINK.ViewModel.Bikes.Bike; using TINK.ViewModel.Bikes.Bike;
@ -8,7 +7,7 @@ namespace TINK.ViewModel
public class BikeAtStationInUseStateInfoProvider : IInUseStateInfoProvider public class BikeAtStationInUseStateInfoProvider : IInUseStateInfoProvider
{ {
/// <summary> Gets reserved into display text. </summary> /// <summary> Gets reserved into display text. </summary>
/// <todo>Log unexpeced states.</todo> /// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns> /// <returns>Display text</returns>
public string GetReservedInfo( public string GetReservedInfo(
TimeSpan? remainingTime, TimeSpan? remainingTime,
@ -21,10 +20,10 @@ namespace TINK.ViewModel
if (string.IsNullOrEmpty(code)) if (string.IsNullOrEmpty(code))
{ {
// Reservation code not available // Reservation code not available
return string.Format(AppResources.StatusTextReservationExpiredMaximumReservationTime, StateRequestedInfo.MaximumReserveTime.Minutes); return AppResources.StatusTextReservationExpiredMaximumReservationTime;
} }
return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code, StateRequestedInfo.MaximumReserveTime.Minutes); return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code);
} }
if (!string.IsNullOrEmpty(code)) if (!string.IsNullOrEmpty(code))
@ -36,7 +35,7 @@ namespace TINK.ViewModel
} }
/// <summary> Gets booked into display text. </summary> /// <summary> Gets booked into display text. </summary>
/// <todo>Log unexpeced states.</todo> /// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns> /// <returns>Display text</returns>
public string GetBookedInfo( public string GetBookedInfo(
DateTime? from, DateTime? from,

View file

@ -10,10 +10,13 @@ using Plugin.BLE.Abstractions.Contracts;
using Serilog; using Serilog;
using TINK.Model; using TINK.Model;
using TINK.Model.Bikes; using TINK.Model.Bikes;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
using TINK.Model.Connector; using TINK.Model.Connector;
using TINK.Model.Connector.Filter;
using TINK.Model.Device; using TINK.Model.Device;
using TINK.Model.Services.CopriApi; using TINK.Model.Services.CopriApi;
using TINK.Model.State;
using TINK.Model.Stations.StationNS; using TINK.Model.Stations.StationNS;
using TINK.Model.User; using TINK.Model.User;
using TINK.MultilingualResources; using TINK.MultilingualResources;
@ -25,6 +28,7 @@ using TINK.Services.Permissions;
using TINK.Settings; using TINK.Settings;
using TINK.View; using TINK.View;
using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes;
using TINK.ViewModel.Map;
using Xamarin.Essentials; using Xamarin.Essentials;
using Xamarin.Forms; using Xamarin.Forms;
using Command = Xamarin.Forms.Command; using Command = Xamarin.Forms.Command;
@ -84,13 +88,13 @@ namespace TINK.ViewModel.FindBike
/// <summary> /// <summary>
/// True if ListView of Bikes is refreshing after user pulled; /// True if ListView of Bikes is refreshing after user pulled;
/// </summary> /// </summary>
private bool _isRefreshing = false; private bool isRefreshing = false;
public bool IsRefreshing public bool IsRefreshing
{ {
get { return _isRefreshing; } get { return isRefreshing; }
set set
{ {
_isRefreshing = value; isRefreshing = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing))); OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing)));
} }
} }
@ -99,6 +103,7 @@ namespace TINK.ViewModel.FindBike
/// Holds what should be executed on pull to refresh /// Holds what should be executed on pull to refresh
/// </summary> /// </summary>
public Command RefreshCommand { get; } public Command RefreshCommand { get; }
public Command ShowFilterBikeTypeInfoCommand { get; private set; }
/// <summary> /// <summary>
/// Constructs bike collection view model in case information about occupied bikes is available. /// Constructs bike collection view model in case information about occupied bikes is available.
@ -146,6 +151,15 @@ namespace TINK.ViewModel.FindBike
await SelectBike(); await SelectBike();
}); });
ShowFilterBikeTypeInfoCommand = new Xamarin.Forms.Command(async () => {
await ViewService.DisplayAlert(
AppResources.MessageBikeTypeInfoTitle,
AppResources.MessageBikeTypeInfoText,
AppResources.MessageAnswerOk);
});
} }
/// <summary> /// <summary>
@ -158,6 +172,8 @@ namespace TINK.ViewModel.FindBike
Log.ForContext<FindBikePageViewModel>().Information("User request to show page FindBike- page re-appearing"); Log.ForContext<FindBikePageViewModel>().Information("User request to show page FindBike- page re-appearing");
ActiveFilteredBikeType = string.Empty;
IsConnected = IsConnectedDelegate(); IsConnected = IsConnectedDelegate();
// Stop polling before getting bikes info. // Stop polling before getting bikes info.
@ -185,6 +201,19 @@ namespace TINK.ViewModel.FindBike
ActionText = string.Empty; ActionText = string.Empty;
IsIdle = true; IsIdle = true;
var result = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
var bikes = result.Response;
var exception = result.Exception;
if (exception != null)
{
Log.ForContext<MapPageViewModel>().Error("Getting bikes in polling context failed with exception {Exception}.", exception);
}
// Get Active Filtered BikeType
GetActiveFilteredBikeType(bikes);
} }
/// <summary> Command object to bind select bike button to view model. </summary> /// <summary> Command object to bind select bike button to view model. </summary>
@ -209,7 +238,7 @@ namespace TINK.ViewModel.FindBike
if (exception is WebConnectFailureException) if (exception is WebConnectFailureException)
{ {
// Copri server is not reachable. // Copri server is not reachable.
Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed failed (Copri server not reachable)."); Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed (Copri server not reachable).");
await ViewService.DisplayAdvancedAlert( await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle, AppResources.ErrorReturnBikeNoWebTitle,
@ -402,6 +431,9 @@ namespace TINK.ViewModel.FindBike
IsIdle = true; IsIdle = true;
return; return;
} }
BikeIdUserInput = string.Empty;
} }
/// <summary> Create task which updates my bike view model.</summary> /// <summary> Create task which updates my bike view model.</summary>
@ -417,13 +449,12 @@ namespace TINK.ViewModel.FindBike
null); null);
var result = ConnectorFactory(IsConnected).Query.GetBikesAsync().Result; var result = ConnectorFactory(IsConnected).Query.GetBikesAsync().Result;
var bikes = result.Response; var bikes = result.Response;
var exception = result.Exception; var exception = result.Exception;
if (exception != null) if (exception != null)
{ {
Log.ForContext<FindBikePageViewModel>().Error("Getting bikes occupied in polling context failed with exception {Exception}.", exception); Log.ForContext<FindBikePageViewModel>().Error("Getting bikes in polling context failed with exception {Exception}.", exception);
} }
var selectedBike = bikes.FirstOrDefault(x => x.Id.Equals(BikeIdUserInput.Trim(), StringComparison.OrdinalIgnoreCase)); var selectedBike = bikes.FirstOrDefault(x => x.Id.Equals(BikeIdUserInput.Trim(), StringComparison.OrdinalIgnoreCase));
@ -441,5 +472,44 @@ namespace TINK.ViewModel.FindBike
}, },
null); null);
} }
private string activeFilteredBikeType = string.Empty;
/// <summary>
/// Selected Bike Type in MapFilter
/// </summary>
public string ActiveFilteredBikeType
{
get { return activeFilteredBikeType; }
set
{
if (value == activeFilteredBikeType)
{
return;
}
activeFilteredBikeType = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(ActiveFilteredBikeType)));
}
}
/// <summary>
/// Get Selected Bike Type in MapFilter
/// </summary>
public void GetActiveFilteredBikeType(BikeCollection bikesAll)
{
Log.ForContext<FindBikePageViewModel>().Debug($"Bike type of active filter is extracted.");
if (bikesAll != null)
{
var firstOrDefaultBikeType = bikesAll.FirstOrDefault().TypeOfBike;
if(firstOrDefaultBikeType == TypeOfBike.Cargo)
{
ActiveFilteredBikeType = AppResources.MarkingCargoBike;
}
else if(firstOrDefaultBikeType == TypeOfBike.City)
{
ActiveFilteredBikeType = AppResources.MarkingCityBike;
}
}
}
} }
} }

View file

@ -1,4 +1,4 @@
using System; using System;
namespace TINK.ViewModel namespace TINK.ViewModel
{ {

View file

@ -50,7 +50,6 @@ namespace TINK.ViewModel.Map
/// </summary> /// </summary>
private Exception m_oException; private Exception m_oException;
/// <summary> /// <summary>
/// Service to query/ manage permissions (location) of the app. /// Service to query/ manage permissions (location) of the app.
/// </summary> /// </summary>
@ -483,10 +482,23 @@ namespace TINK.ViewModel.Map
Log.ForContext<MapPageViewModel>().Verbose("Location permissions: {0}.", status); Log.ForContext<MapPageViewModel>().Verbose("Location permissions: {0}.", status);
} }
private bool isLocationPermissionGranted = false;
/// <summary> /// <summary>
/// Exposes IsLocationPermissionGranted. /// Exposes IsLocationPermissionGranted.
/// </summary> /// </summary>
public bool IsLocationPermissionGranted{ get; set;} public bool IsLocationPermissionGranted
{
get => isLocationPermissionGranted;
set
{
if (value == isLocationPermissionGranted)
return;
Log.ForContext<MapPageViewModel>().Debug($"Switch value of {nameof(isLocationPermissionGranted)} to {value}.");
isLocationPermissionGranted = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLocationPermissionGranted)));
}
}
/// <summary> /// <summary>
/// Invoked when the auth cookie is not defined. /// Invoked when the auth cookie is not defined.
@ -655,6 +667,9 @@ namespace TINK.ViewModel.Map
Log.ForContext<MapPageViewModel>().Error("Getting bikes and stations in polling context failed with exception {Exception}.", exception); Log.ForContext<MapPageViewModel>().Error("Getting bikes and stations in polling context failed with exception {Exception}.", exception);
} }
// Get and expose status of location permission
GetLocationPermissionStatus();
// Load MyBikes Count -> MyBikes Icon/Button // Load MyBikes Count -> MyBikes Icon/Button
GetMyBikesCount(resultStationsAndBikes.Response.BikesOccupied); GetMyBikesCount(resultStationsAndBikes.Response.BikesOccupied);

View file

@ -8,7 +8,7 @@ namespace TINK.ViewModel
public class MyBikeInUseStateInfoProvider : IInUseStateInfoProvider public class MyBikeInUseStateInfoProvider : IInUseStateInfoProvider
{ {
/// <summary> Gets reserved into display text. </summary> /// <summary> Gets reserved into display text. </summary>
/// <todo>Log unexpeced states.</todo> /// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns> /// <returns>Display text</returns>
public string GetReservedInfo( public string GetReservedInfo(
TimeSpan? remainingTime, TimeSpan? remainingTime,
@ -17,25 +17,25 @@ namespace TINK.ViewModel
{ {
if (remainingTime == null) if (remainingTime == null)
{ {
// Reamining time is not available. // Remaining time is not available.
if (stationId == null) if (stationId == null)
{ {
if (string.IsNullOrEmpty(code)) if (string.IsNullOrEmpty(code))
{ {
// Code is not available // Code is not available
return string.Format(AppResources.StatusTextReservationExpiredMaximumReservationTime, StateRequestedInfo.MaximumReserveTime.Minutes); return AppResources.StatusTextReservationExpiredMaximumReservationTime;
} }
return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code, StateRequestedInfo.MaximumReserveTime.Minutes); return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code);
} }
if (string.IsNullOrEmpty(code)) if (string.IsNullOrEmpty(code))
{ {
return string.Format(AppResources.StatusTextReservationExpiredLocationMaxReservationTime, stationId, StateRequestedInfo.MaximumReserveTime.Minutes); return string.Format(AppResources.StatusTextReservationExpiredLocationMaxReservationTime, stationId);
} }
return string.Format(AppResources.StatusTextReservationExpiredCodeLocationMaxReservationTime, code, stationId, StateRequestedInfo.MaximumReserveTime.Minutes); return string.Format(AppResources.StatusTextReservationExpiredCodeLocationMaxReservationTime, code, stationId);
} }
if (!string.IsNullOrEmpty(stationId)) if (!string.IsNullOrEmpty(stationId))
@ -63,7 +63,7 @@ namespace TINK.ViewModel
/// <summary> /// <summary>
/// Gets booked into display text. /// Gets booked into display text.
/// </summary> /// </summary>
/// <todo>Log unexpeced states.</todo> /// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns> /// <returns>Display text</returns>
public string GetBookedInfo( public string GetBookedInfo(
DateTime? from, DateTime? from,

View file

@ -12,8 +12,8 @@ namespace TINK.ViewModel
/// </summary> /// </summary>
public class PollingUpdateTask public class PollingUpdateTask
{ {
/// <summary> Object to control canelling. </summary> /// <summary> Object to control canceling. </summary>
private CancellationTokenSource CanellationTokenSource { get; } private CancellationTokenSource CancellationTokenSource { get; }
/// <summary> Task to perform update. </summary> /// <summary> Task to perform update. </summary>
private Task UpdateTask { get; } private Task UpdateTask { get; }
@ -40,15 +40,15 @@ namespace TINK.ViewModel
var updatePeriodeSet = polling.Value; var updatePeriodeSet = polling.Value;
CanellationTokenSource = new CancellationTokenSource(); CancellationTokenSource = new CancellationTokenSource();
int cycleIndex = 2; int cycleIndex = 2;
UpdateTask = Task.Run( UpdateTask = Task.Run(
async () => async () =>
{ {
while (!CanellationTokenSource.IsCancellationRequested) while (!CancellationTokenSource.IsCancellationRequested)
{ {
await Task.Delay(updatePeriodeSet, CanellationTokenSource.Token); await Task.Delay(updatePeriodeSet, CancellationTokenSource.Token);
{ {
// N. update cycle // N. update cycle
Log.ForContext<PollingUpdateTask>().Information($"Actuating {cycleIndex} update cycle, context {GetType().Name} at {DateTime.Now}."); Log.ForContext<PollingUpdateTask>().Information($"Actuating {cycleIndex} update cycle, context {GetType().Name} at {DateTime.Now}.");
@ -60,7 +60,7 @@ namespace TINK.ViewModel
} }
} }
}, },
CanellationTokenSource.Token); CancellationTokenSource.Token);
} }
/// <summary> /// <summary>
@ -76,13 +76,13 @@ namespace TINK.ViewModel
} }
// Cancel update task; // Cancel update task;
if (CanellationTokenSource == null) if (CancellationTokenSource == null)
{ {
throw new Exception($"Can not terminate periodical update task, context {GetType().Name} at {DateTime.Now}. No task running."); throw new Exception($"Can not terminate periodical update task, context {GetType().Name} at {DateTime.Now}. No task running.");
} }
Log.ForContext<PollingUpdateTask>().Information($"Request to terminate update cycle, context {GetType().Name} at {DateTime.Now}."); Log.ForContext<PollingUpdateTask>().Information($"Request to terminate update cycle, context {GetType().Name} at {DateTime.Now}.");
CanellationTokenSource.Cancel(); CancellationTokenSource.Cancel();
try try
{ {

View file

@ -45,7 +45,7 @@ namespace UITest.Fixtures.ViewModel
} }
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
public static BikeViewModelBase TestStateText_LoggedIn_Reserved(Func<TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable, User, BikeViewModelBase> p_oFactory) public static BikeViewModelBase TestStateText_LoggedIn_Reserved(Func<TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable, User, BikeViewModelBase> p_oFactory)
{ {
@ -67,6 +67,7 @@ namespace UITest.Fixtures.ViewModel
l_oBike.State.Load( l_oBike.State.Load(
InUseStateEnum.Reserved, // Copri acknowledges state reserved. InUseStateEnum.Reserved, // Copri acknowledges state reserved.
new DateTime(1980, 1, 1), // Date when bike was booked. new DateTime(1980, 1, 1), // Date when bike was booked.
TimeSpan.FromMinutes(15),
"ragu@gnu-systems.de"); // Owner from Copri. "ragu@gnu-systems.de"); // Owner from Copri.
var l_oStoreMock = new StoreMock(new Account("ragu@gnu-systems.de", "123456789" /* password */, false, "987654321" /* session cookie */, new List<string> { "TINK" })); var l_oStoreMock = new StoreMock(new Account("ragu@gnu-systems.de", "123456789" /* password */, false, "987654321" /* session cookie */, new List<string> { "TINK" }));
@ -87,7 +88,7 @@ namespace UITest.Fixtures.ViewModel
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
public static BikeViewModelBase TestStateText_LoggedIn_ReservedWithCopriConnect(Func<TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable, User, BikeViewModelBase> p_oFactory) public static BikeViewModelBase TestStateText_LoggedIn_ReservedWithCopriConnect(Func<TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable, User, BikeViewModelBase> p_oFactory)
{ {
@ -109,6 +110,7 @@ namespace UITest.Fixtures.ViewModel
l_oBike.State.Load( l_oBike.State.Load(
InUseStateEnum.Reserved, // Copri acknowledges state reserved. InUseStateEnum.Reserved, // Copri acknowledges state reserved.
new DateTime(1980, 1, 1), new DateTime(1980, 1, 1),
TimeSpan.FromMinutes(15),
"ragu@gnu-systems.de", // Owner from Copri. "ragu@gnu-systems.de", // Owner from Copri.
"4asdfA"); // Reservation code from Copri "4asdfA"); // Reservation code from Copri
@ -129,7 +131,7 @@ namespace UITest.Fixtures.ViewModel
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
public static BikeViewModelBase TestStateText_LoggedIn_Booked(Func<TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable, User, BikeViewModelBase> p_oFactory) public static BikeViewModelBase TestStateText_LoggedIn_Booked(Func<TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable, User, BikeViewModelBase> p_oFactory)
{ {
@ -139,6 +141,7 @@ namespace UITest.Fixtures.ViewModel
l_oBike.State.Load( l_oBike.State.Load(
InUseStateEnum.Booked, InUseStateEnum.Booked,
new DateTime(2017, 10, 24, 21, 49, 3), new DateTime(2017, 10, 24, 21, 49, 3),
TimeSpan.FromMinutes(15),
"ragu@gnu-systems.de", "ragu@gnu-systems.de",
"4asdfA"); "4asdfA");

View file

@ -65,7 +65,7 @@ namespace TestTINKLib
public void TestUpdate_Null() public void TestUpdate_Null()
{ {
var l_oBikeRequested = new BikeInfoMutable("20", LockModel.ILockIt, false, new List<string> { "TINK" }, WheelType.Trike, TypeOfBike.Allround); var l_oBikeRequested = new BikeInfoMutable("20", LockModel.ILockIt, false, new List<string> { "TINK" }, WheelType.Trike, TypeOfBike.Allround);
l_oBikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, "john@long", "1234")); l_oBikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, TimeSpan.FromMinutes(15), "john@long", "1234"));
var l_oBikeColl = new BikeCollectionMutable var l_oBikeColl = new BikeCollectionMutable
{ {
@ -93,7 +93,7 @@ namespace TestTINKLib
public void TestUpdate() public void TestUpdate()
{ {
var bikeRequested = new BikeInfoMutable("20", LockModel.ILockIt, false, new List<string> { "TINK" }, WheelType.Trike, TypeOfBike.Allround); var bikeRequested = new BikeInfoMutable("20", LockModel.ILockIt, false, new List<string> { "TINK" }, WheelType.Trike, TypeOfBike.Allround);
bikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, "john@long", "1234")); bikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, TimeSpan.FromMinutes(15), "john@long", "1234"));
var bikeColl = new BikeCollectionMutable var bikeColl = new BikeCollectionMutable
{ {

View file

@ -322,5 +322,15 @@ namespace TestShareeLib.Model.Connector
Is.EqualTo(TINK.Model.State.InUseStateEnum.FeedbackPending), Is.EqualTo(TINK.Model.State.InUseStateEnum.FeedbackPending),
"Bikes with state booking state available in "); "Bikes with state booking state available in ");
} }
}
[Test]
public void TestReservationTimeSpan()
{
var response = JsonConvertRethrow.DeserializeObject<RentalDescription>(TestShareeLib.Repository.Response.TestRentalDescription.RENTALDESCRIPTIONRESPONSE_A_V4_1_23_03);
var rentalDescription = RentalDescriptionFactory.Create(response);
Assert.That(rentalDescription.MaxReservationTimeSpan.TotalMinutes, Is.EqualTo(30));
}
}
} }

View file

@ -1055,5 +1055,30 @@ namespace TestTINKLib.Fixtures.Connector
{ {
}").GetFrom(), }").GetFrom(),
Is.EqualTo(DateTime.MinValue)); Is.EqualTo(DateTime.MinValue));
[Test]
public void TestGetReservationTimeSpan()
{
var response = JsonConvertRethrow.DeserializeObject<RentalDescription>(TestShareeLib.Repository.Response.TestRentalDescription.RENTALDESCRIPTIONRESPONSE_A_V4_1_23_03);
Assert.That(response.GetMaxReservationTimeSpan().TotalMinutes, Is.EqualTo(30));
}
[Test]
public void TestGetReservationTimeSpanInvalid()
=> Assert.That(
JsonConvertRethrow.DeserializeObject<RentalDescription>(@"
{
""reserve_timerange"": ""abc"",
}").GetMaxReservationTimeSpan().TotalMinutes,
Is.EqualTo(15));
[Test]
public void TestGetReservationTimeSpanEmpty()
=> Assert.That(
JsonConvertRethrow.DeserializeObject<RentalDescription>(@"
{
}").GetMaxReservationTimeSpan().TotalMinutes,
Is.EqualTo(15));
} }
} }

View file

@ -1,4 +1,4 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using NUnit.Framework; using NUnit.Framework;
using TINK.Model.Connector.Updater; using TINK.Model.Connector.Updater;
using TINK.Repository.Response; using TINK.Repository.Response;

View file

@ -44,6 +44,7 @@ namespace TestTINKLib
l_oSource.From.Returns(new DateTime(2018, 1, 4, 17, 26, 0)); l_oSource.From.Returns(new DateTime(2018, 1, 4, 17, 26, 0));
l_oSource.MailAddress.Returns("who@the"); l_oSource.MailAddress.Returns("who@the");
l_oSource.Code.Returns("323"); l_oSource.Code.Returns("323");
l_oSource.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
l_oState = new StateInfoMutable(() => new DateTime(2018, 1, 4, 17, 30, 1), l_oSource); l_oState = new StateInfoMutable(() => new DateTime(2018, 1, 4, 17, 30, 1), l_oSource);
@ -85,6 +86,7 @@ namespace TestTINKLib
l_oStateInfo.Load( l_oStateInfo.Load(
InUseStateEnum.Reserved, // Copri acknowledges state reserved. InUseStateEnum.Reserved, // Copri acknowledges state reserved.
l_oDateTimeMock.GetDateTime(), l_oDateTimeMock.GetDateTime(),
TimeSpan.FromMinutes(15),
"heiz@mustermann"); // Owner from Copri. "heiz@mustermann"); // Owner from Copri.
// Invoke first update (after simulated 4mns) // Invoke first update (after simulated 4mns)
@ -110,7 +112,7 @@ namespace TestTINKLib
Func<DateTime> l_oNowMock = () => new DateTime(2017, 09, 21, 23, 40, 0); Func<DateTime> l_oNowMock = () => new DateTime(2017, 09, 21, 23, 40, 0);
var l_oStateInfo = new StateInfoMutable(l_oNowMock); var l_oStateInfo = new StateInfoMutable(l_oNowMock);
l_oStateInfo.Load(InUseStateEnum.Booked, new DateTime(2017, 09, 21, 23, 30, 0), "heiz@mustermann", "21"); l_oStateInfo.Load(InUseStateEnum.Booked, new DateTime(2017, 09, 21, 23, 30, 0), TimeSpan.FromMinutes(15), "heiz@mustermann", "21");
Assert.AreEqual(InUseStateEnum.Booked, l_oStateInfo.Value); Assert.AreEqual(InUseStateEnum.Booked, l_oStateInfo.Value);
Assert.AreEqual("Booked", l_oStateInfo.ToString()); Assert.AreEqual("Booked", l_oStateInfo.ToString());
@ -120,7 +122,7 @@ namespace TestTINKLib
Assert.AreEqual("21", l_oStateInfo.Code); Assert.AreEqual("21", l_oStateInfo.Code);
DateTime FROM = new DateTime(2017, 09, 21, 23, 35, 0); DateTime FROM = new DateTime(2017, 09, 21, 23, 35, 0);
l_oStateInfo.Load(InUseStateEnum.Reserved, FROM, "heiz@mustermann", "22"); l_oStateInfo.Load(InUseStateEnum.Reserved, FROM, TimeSpan.FromMinutes(15), "heiz@mustermann", "22");
// Verify initial values of properties. // Verify initial values of properties.
Assert.AreEqual(InUseStateEnum.Reserved, l_oStateInfo.Value); Assert.AreEqual(InUseStateEnum.Reserved, l_oStateInfo.Value);
@ -130,7 +132,7 @@ namespace TestTINKLib
Assert.AreEqual("heiz@mustermann", l_oStateInfo.MailAddress); Assert.AreEqual("heiz@mustermann", l_oStateInfo.MailAddress);
Assert.AreEqual("22", l_oStateInfo.Code); Assert.AreEqual("22", l_oStateInfo.Code);
l_oStateInfo.Load(InUseStateEnum.Disposable, new DateTime(1970, 1, 1, 0, 0, 0), "heiz@mustermann", "unused"); l_oStateInfo.Load(InUseStateEnum.Disposable, new DateTime(1970, 1, 1, 0, 0, 0), TimeSpan.FromMinutes(15), "heiz@mustermann", "unused");
// Verify initial values of properties. // Verify initial values of properties.
Assert.AreEqual(InUseStateEnum.Disposable, l_oStateInfo.Value); Assert.AreEqual(InUseStateEnum.Disposable, l_oStateInfo.Value);
@ -156,6 +158,7 @@ namespace TestTINKLib
var l_oSource = new StateInfo( var l_oSource = new StateInfo(
() => new DateTime(2018, 01, 03, 21, 53, 0), () => new DateTime(2018, 01, 03, 21, 53, 0),
new DateTime(2018, 01, 03, 21, 13, 0), // Requested from new DateTime(2018, 01, 03, 21, 13, 0), // Requested from
TimeSpan.FromMinutes(15),
"a@b", "a@b",
"123"); "123");
l_oState.Load(l_oSource); l_oState.Load(l_oSource);
@ -184,5 +187,51 @@ namespace TestTINKLib
Assert.IsNull(l_oState.MailAddress); Assert.IsNull(l_oState.MailAddress);
Assert.IsNull(l_oState.Code); Assert.IsNull(l_oState.Code);
} }
[Test]
public void TestLoadFromNull()
{
var state = new StateInfoMutable(
() => new DateTime(2018, 01, 03, 21, 14, 0));
state.Load(InUseStateEnum.Disposable,
null,
TimeSpan.FromMinutes(15));
Assert.That(
state.RemainingTime,
Is.Null);
}
[Test]
public void TestLoadReservationTimeSpanNull()
{
var state = new StateInfoMutable(
() => new DateTime(2018, 01, 03, 21, 14, 0));
state.Load(InUseStateEnum.Disposable,
DateTime.Now,
null);
Assert.That(
state.RemainingTime,
Is.Null);
}
[Test]
public void TestMaxReservationTimeSpanNullStateAvaialble()
=> Assert.That(new StateInfo().MaxReservationTimeSpan, Is.Null);
[Test]
public void TestMaxReservationTimeSpanNullStateFeedbackPending()
=> Assert.That(new StateInfo(true).MaxReservationTimeSpan, Is.Null);
[Test]
public void TestMaxReservationTimeSpanNullStateOccupied()
=> Assert.That(new StateInfo(new DateTime(2023, 5, 11), "a@b", "").MaxReservationTimeSpan, Is.Null);
[Test]
public void TestMaxReservationTimeSpan()
=> Assert.That(new StateInfo(() => DateTime.Now, new DateTime(2023, 5, 11), TimeSpan.FromMinutes(12), "a@b", "").MaxReservationTimeSpan, Is.EqualTo(TimeSpan.FromMinutes(12)));
} }
} }

View file

@ -55,13 +55,13 @@ namespace TestTINKLib.Fixtures.State
public void TestSerializeJSON_Booked() public void TestSerializeJSON_Booked()
{ {
// Create object to test. // Create object to test.
var l_oInUseState = new StateInfoMutable( var l_oInUseState = new StateInfoMutable(() => new DateTime(2017, 11, 18, 23, 20, 0) // Mocked time stamp returned when StateInfo- object is crated
() => new DateTime(2017, 11, 18, 23, 20, 0) // Mocked time stamp returned when StateInfo- object is crated
); );
l_oInUseState.Load( l_oInUseState.Load(
InUseStateEnum.Booked, InUseStateEnum.Booked,
new DateTime(2017, 11, 18, 23, 19, 0), // Time booked at new DateTime(2017, 11, 18, 23, 19, 0), // Time booked at
TimeSpan.FromMinutes(15),
"heiz@mustermann", "heiz@mustermann",
"173"); // Code "173"); // Code
@ -111,6 +111,7 @@ namespace TestTINKLib.Fixtures.State
l_oInUseState.Load( l_oInUseState.Load(
InUseStateEnum.Reserved, InUseStateEnum.Reserved,
new DateTime(2017, 09, 21, 23, 20, 0), new DateTime(2017, 09, 21, 23, 20, 0),
TimeSpan.FromMinutes(15),
"heiz@mustermann"); "heiz@mustermann");
Assert.AreEqual(InUseStateEnum.Reserved, l_oInUseState.Value); Assert.AreEqual(InUseStateEnum.Reserved, l_oInUseState.Value);
@ -167,6 +168,7 @@ namespace TestTINKLib.Fixtures.State
l_oInUseState.Load( l_oInUseState.Load(
InUseStateEnum.Reserved, InUseStateEnum.Reserved,
l_oFrom, l_oFrom,
TimeSpan.FromMinutes(15),
"z@C", "z@C",
"01815A"); "01815A");

View file

@ -15,25 +15,25 @@ namespace TestTINKLib
{ {
Assert.AreEqual( Assert.AreEqual(
InUseStateEnum.Reserved, InUseStateEnum.Reserved,
new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), "a@b", null).Value); new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), TimeSpan.FromMinutes(15), "a@b", null).Value);
Assert.AreEqual( Assert.AreEqual(
"a@b", "a@b",
new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), "a@b", null).MailAddress); new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), TimeSpan.FromMinutes(15), "a@b", null).MailAddress);
Assert.IsNull( Assert.IsNull(
new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), "a@b", null).Code); new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), TimeSpan.FromMinutes(15), "a@b", null).Code);
Assert.AreEqual( Assert.AreEqual(
new DateTime(2017, 09, 20, 12, 0, 0), new DateTime(2017, 09, 20, 12, 0, 0),
new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), "a@b", null).From); new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), TimeSpan.FromMinutes(15), "a@b", null).From);
Assert.IsTrue( Assert.IsTrue(
new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), "a@b", null).GetIsStillReserved(out TimeSpan? remainingTime)); new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 1, 0), new DateTime(2017, 09, 20, 12, 0, 0), TimeSpan.FromMinutes(15), "a@b", null).GetIsStillReserved(out TimeSpan? l_oRemainigTime));
Assert.AreEqual( Assert.AreEqual(
14, 14,
remainingTime.Value.Minutes); l_oRemainigTime.Value.Minutes);
} }
[Test] [Test]
@ -41,26 +41,26 @@ namespace TestTINKLib
{ {
Assert.AreEqual( Assert.AreEqual(
InUseStateEnum.Reserved, InUseStateEnum.Reserved,
new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), "a@b", "372").Value); new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), TimeSpan.FromMinutes(15), "a@b", "372").Value);
Assert.AreEqual( Assert.AreEqual(
"a@b", "a@b",
new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), "a@b", "372").MailAddress); new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), TimeSpan.FromMinutes(15), "a@b", "372").MailAddress);
Assert.AreEqual( Assert.AreEqual(
"372", "372",
new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), "a@b", "372").Code); new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), TimeSpan.FromMinutes(15), "a@b", "372").Code);
Assert.AreEqual( Assert.AreEqual(
new DateTime(2017, 09, 19), new DateTime(2017, 09, 19),
new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), "a@b", "372").From); new StateRequestedInfo(() => new DateTime(2017, 09, 20), new DateTime(2017, 09, 19), TimeSpan.FromMinutes(15), "a@b", "372").From);
Assert.IsTrue( Assert.IsTrue(
new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 12, 0), new DateTime(2017, 09, 20, 12, 0, 0), "a@b", "372").GetIsStillReserved(out TimeSpan? remainingTime)); new StateRequestedInfo(() => new DateTime(2017, 09, 20, 12, 12, 0), new DateTime(2017, 09, 20, 12, 0, 0), TimeSpan.FromMinutes(15), "a@b", "372").GetIsStillReserved(out TimeSpan? l_oRemainigTime));
Assert.AreEqual( Assert.AreEqual(
3, 3,
remainingTime.Value.Minutes); l_oRemainigTime.Value.Minutes);
} }
[Test] [Test]
@ -72,7 +72,7 @@ namespace TestTINKLib
l_oReservedAt.Add(new TimeSpan(0, 16, 0)) // Time elapsed since booking 16 mns l_oReservedAt.Add(new TimeSpan(0, 16, 0)) // Time elapsed since booking 16 mns
}); });
var l_oReservedInfo = new StateRequestedInfo(l_oDateTimeMock.GetDateTime, l_oReservedAt, "a@b", null); var l_oReservedInfo = new StateRequestedInfo(l_oDateTimeMock.GetDateTime, l_oReservedAt, TimeSpan.FromMinutes(15), "a@b", null);
Assert.AreEqual("a@b", l_oReservedInfo.MailAddress); Assert.AreEqual("a@b", l_oReservedInfo.MailAddress);
Assert.AreEqual(new DateTime(2017, 09, 20, 22, 01, 00), l_oReservedInfo.From, "a@b"); Assert.AreEqual(new DateTime(2017, 09, 20, 22, 01, 00), l_oReservedInfo.From, "a@b");
@ -86,5 +86,39 @@ namespace TestTINKLib
Assert.IsNull(remainingTime); Assert.IsNull(remainingTime);
Assert.AreEqual(new DateTime(2017, 09, 20, 22, 01, 00), l_oReservedInfo.From, "a@b"); Assert.AreEqual(new DateTime(2017, 09, 20, 22, 01, 00), l_oReservedInfo.From, "a@b");
} }
[Test]
public void TestReservationTimeSpanCtor() =>
Assert.That(new StateRequestedInfo().MaxReservationTimeSpan, Is.EqualTo(TimeSpan.Zero));
[Test]
public void TestGetIsStillReserved() =>
Assert.That(new StateRequestedInfo(
() => new DateTime(2023, 5, 11, 14, 42, 0, 100), // one hundred millisecond after bike was reserved
new DateTime(2023, 5, 11, 14, 42, 0, 0),
TimeSpan.FromSeconds(1),
"a@b",
"").GetIsStillReserved(out _),
Is.True);
[Test]
public void TestGetIsStillReservedExpired() =>
Assert.That(new StateRequestedInfo(
() => new DateTime(2023, 5, 11, 14, 42, 0, 100), // one hundred millisecond after bike was reserved
new DateTime(2023, 5, 11, 14, 42, 0, 0),
TimeSpan.FromMilliseconds(50),
"a@b",
"").GetIsStillReserved(out _),
Is.False);
[Test]
public void TestGetIsStillReservedInvalidMaxReservationTimeSpan() =>
Assert.That(new StateRequestedInfo(
() => new DateTime(2023, 5, 11, 14, 42, 0, 1), // one hundred millisecond after bike was reserved
new DateTime(2023, 5, 11, 14, 42, 0, 0),
TimeSpan.Zero,
"a@b",
"").GetIsStillReserved(out _),
Is.False);
} }
} }

View file

@ -18,6 +18,7 @@ namespace TestTINKLib.Fixtures.State
var l_oReservedInfo = new StateRequestedInfo( var l_oReservedInfo = new StateRequestedInfo(
() => new DateTime(2017, 09, 20), () => new DateTime(2017, 09, 20),
new DateTime(2017, 09, 19), new DateTime(2017, 09, 19),
TimeSpan.FromMinutes(15),
"ä@b", "ä@b",
"372"); "372");

View file

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using TINK.Repository.Response; using TINK.Repository.Response;
@ -129,7 +129,7 @@ namespace TestShareeLib.Repository.Response
} }
/// <summary> /// <summary>
/// Verifies that unknown elemts in JSON to not lead to firing of exceptions. /// Verifies that unknown elements in JSON to not lead to firing of exceptions.
/// </summary> /// </summary>
[Test] [Test]
public void TestDeserialize_NewElement() public void TestDeserialize_NewElement()
@ -186,7 +186,47 @@ namespace TestShareeLib.Repository.Response
Assert.That( Assert.That(
tariffDescription.tarif_elements.Count, tariffDescription.tarif_elements.Count,
Is.EqualTo(2), Is.EqualTo(2),
"Dupliate elements are supposed to be ignored."); "Duplicate elements are supposed to be ignored.");
} }
public static string RENTALDESCRIPTIONRESPONSE_A_V4_1_23_03 = @"{
""tarif_elements"": {
""1"": [
""Mietgebühr"",
""1,50 / 30 Min ""
],
""4"": [
""Max. Gebühr"",
""25,00 / Tag""
],
""6"": [
""Gratis Mietzeit"",
""30 Min / Tag""
]
},
""reserve_timerange"": ""30"",
""id"": ""100"",
""name"": ""Basis"",
""rental_info"": {
""1"": [
""Tracking"",
""Ich stimme der Speicherung (Tracking) meiner Fahrstrecke zwecks wissenschaftlicher Auswertung und Berechnung der CO2-Einsparung zu!""
],
""2"": [
""AAFahrten"",
""Dieses E-Lastenrad darf nur an der Station zurück gegeben werden an der es ausgeliehen wurde!""
]
}
}";
[Test]
public void TestReserveTimeRange()
{
var response = JsonConvertRethrow.DeserializeObject<RentalDescription>(RENTALDESCRIPTIONRESPONSE_A_V4_1_23_03);
Assert.That(
response.reserve_timerange,
Is.EqualTo("30"));
}
} }
} }

View file

@ -1,4 +1,4 @@
using NUnit.Framework; using NUnit.Framework;
using TINK.Repository.Response; using TINK.Repository.Response;
namespace TestShareeLib.Repository.Response namespace TestShareeLib.Repository.Response
@ -73,7 +73,7 @@ namespace TestShareeLib.Repository.Response
} }
/// <summary> /// <summary>
/// Verifies that unknown elemts in JSON to not lead to firing of exceptions. /// Verifies that unknown elements in JSON to not lead to firing of exceptions.
/// </summary> /// </summary>
[Test] [Test]
public void TestDeserialize_NewElement() public void TestDeserialize_NewElement()

View file

@ -49,7 +49,7 @@ namespace UITest.Fixtures.ViewModel
} }
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
[Test] [Test]
public void TestStateText_NotLoggedIn() public void TestStateText_NotLoggedIn()
@ -89,7 +89,7 @@ namespace UITest.Fixtures.ViewModel
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
[Test] [Test]
public void TestStateText_LoggedIn_Reserved() public void TestStateText_LoggedIn_Reserved()
@ -112,7 +112,7 @@ namespace UITest.Fixtures.ViewModel
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
[Test] [Test]
public void TestStateText_LoggedIn_ReservedWithCopriConnect() public void TestStateText_LoggedIn_ReservedWithCopriConnect()
@ -135,7 +135,7 @@ namespace UITest.Fixtures.ViewModel
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
[Test] [Test]
public void TestStateText_LoggedIn_Booked() public void TestStateText_LoggedIn_Booked()
@ -170,6 +170,7 @@ namespace UITest.Fixtures.ViewModel
l_oBike.State.Load( l_oBike.State.Load(
InUseStateEnum.Reserved, InUseStateEnum.Reserved,
new DateTime(2017, 10, 24, 21, 49, 3), new DateTime(2017, 10, 24, 21, 49, 3),
TimeSpan.FromMinutes(15),
"ragu@gnu-systems.de", "ragu@gnu-systems.de",
"4asdfA"); "4asdfA");
@ -199,7 +200,7 @@ namespace UITest.Fixtures.ViewModel
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
[Test] [Test]
public void TestStateText_LoggedIn_BookedBySomeoneElse() public void TestStateText_LoggedIn_BookedBySomeoneElse()
@ -209,6 +210,7 @@ namespace UITest.Fixtures.ViewModel
l_oBike.State.Load( l_oBike.State.Load(
InUseStateEnum.Booked, InUseStateEnum.Booked,
new DateTime(2017, 10, 24, 21, 49, 3), new DateTime(2017, 10, 24, 21, 49, 3),
TimeSpan.FromMinutes(15),
"ragu@gnu-systems.de", "ragu@gnu-systems.de",
"4asdfA"); "4asdfA");

View file

@ -12,7 +12,7 @@ namespace UITest.Fixtures.ViewModel
public class TestMyBikesPageViewModel public class TestMyBikesPageViewModel
{ {
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
[Test] [Test]
public void TestStateText_LoggedIn_Reserved() public void TestStateText_LoggedIn_Reserved()
@ -36,7 +36,7 @@ namespace UITest.Fixtures.ViewModel
} }
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
[Test] [Test]
public void TestStateText_LoggedIn_ReservedWithCopriConnect() public void TestStateText_LoggedIn_ReservedWithCopriConnect()
@ -60,7 +60,7 @@ namespace UITest.Fixtures.ViewModel
/// <summary> /// <summary>
/// Tests base class functionaltiy by using child. /// Tests base class functionality by using child.
/// </summary> /// </summary>
/// ///
[Test] [Test]

View file

@ -182,7 +182,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
/// <summary> /// <summary>
/// Use case: End rental. /// Use case: End rental.
/// Comment: User deceide to abort returning bike /// Comment: User decide to abort returning bike
/// Final state: Same as initial state. /// Final state: Same as initial state.
/// </summary> /// </summary>
[Test] [Test]
@ -343,7 +343,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
location location
)); ; )); ;
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -410,7 +410,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x => throw new WebConnectFailureException("Context info", new Exception("hoppla"))); connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x => throw new WebConnectFailureException("Context info", new Exception("hoppla")));
bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -486,7 +486,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x => connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x =>
throw notAtStationException); throw notAtStationException);
bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -556,7 +556,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x => connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x =>
throw noGPSDataException); throw noGPSDataException);
bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -624,7 +624,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x => connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x =>
throw new ReturnBikeException(JsonConvert.DeserializeObject<DoReturnResponse>(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); throw new ReturnBikeException(JsonConvert.DeserializeObject<DoReturnResponse>(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message."));
bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -691,7 +691,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x => throw new Exception("Exception message.")); connector.Command.DoReturn(bike, Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;

View file

@ -64,6 +64,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -115,6 +117,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>(); var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -194,6 +198,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -212,7 +218,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReserve(bike).Returns(x => throw new BookingDeclinedException(7)); // Booking must be performed connector.Command.DoReserve(bike).Returns(x => throw new BookingDeclinedException(7)); // Booking must be performed
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -257,6 +263,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -275,7 +283,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReserve(bike).Returns<Task>(x => throw new WebConnectFailureException("Context info.", new Exception("chub"))); connector.Command.DoReserve(bike).Returns<Task>(x => throw new WebConnectFailureException("Context info.", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -320,6 +328,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -338,7 +348,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReserve(bike).Returns<Task>(x => throw new Exception("Exception message.")); connector.Command.DoReserve(bike).Returns<Task>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -384,6 +394,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>(); var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -403,7 +415,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>()).Returns<LockInfoTdo>(x => throw new OutOfReachException()); locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>()).Returns<LockInfoTdo>(x => throw new OutOfReachException());
locks.TimeOut.Returns(timeOuts); locks.TimeOut.Returns(timeOuts);
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -450,6 +462,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>(); var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -469,7 +483,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>()).Returns<LockInfoTdo>(x => throw new Exception("Exception message.")); locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>()).Returns<LockInfoTdo>(x => throw new Exception("Exception message."));
locks.TimeOut.Returns(timeOuts); locks.TimeOut.Returns(timeOuts);
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -516,6 +530,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>(); var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected( var handler = new DisposableDisconnected(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -534,7 +550,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks.TimeOut.Returns(timeOuts); locks.TimeOut.Returns(timeOuts);
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError); // Connect did not throw an exception but lock state is still unknown. bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError); // Connect did not throw an exception but lock state is still unknown.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;

View file

@ -81,7 +81,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync().Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); locks[0].CloseAsync().Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -141,7 +141,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException()); locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException());
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); bike.LockInfo.State.Returns(LockingState.Open);
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -204,7 +204,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message.")); locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); bike.LockInfo.State.Returns(LockingState.Open);
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;

View file

@ -52,7 +52,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
/// <summary> /// <summary>
/// Use case: Cancel reservation. /// Use case: Cancel reservation.
/// Comment: User deceide to abort cancelling (user is ased whether to really cancel) /// Comment: User decide to abort canceling (user is ased whether to really cancel)
/// Final state: Same as initial state. /// Final state: Same as initial state.
/// </summary> /// </summary>
[Test] [Test]
@ -135,7 +135,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
bike.Id.Returns("0"); bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -207,7 +207,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}"); }");
connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", response)); connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", response));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -275,7 +275,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub"))); connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -343,7 +343,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.")); connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;

View file

@ -50,7 +50,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
/// <summary> /// <summary>
/// Use case: Cancel reservation. /// Use case: Cancel reservation.
/// Comment: User deceide to abort cancelling (user is ased whether to really cancel) /// Comment: User decide to abort canceling (user is ased whether to really cancel)
/// Final state: Same as initial state. /// Final state: Same as initial state.
/// </summary> /// </summary>
[Test] [Test]
@ -131,7 +131,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bike.Id.Returns("0"); bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -201,7 +201,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
}"); }");
connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", l_oResponse)); connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", l_oResponse));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -267,7 +267,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info", new Exception("chub"))); connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -333,7 +333,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.")); connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;

View file

@ -80,7 +80,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
viewService.DisplayAlert(string.Empty, "Rad Nr. 0 abschließen und zurückgeben oder Rad mieten?", "Zurückgeben", "Mieten").Returns(Task.FromResult(false)); viewService.DisplayAlert(string.Empty, "Rad Nr. 0 abschließen und zurückgeben oder Rad mieten?", "Zurückgeben", "Mieten").Returns(Task.FromResult(false));
bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); // Requesthandler factory queries lock state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Open); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -143,7 +143,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync().Returns(LockitLockingState.Closed); locks[0].CloseAsync().Returns(LockitLockingState.Closed);
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -212,7 +212,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync().Returns(LockitLockingState.Closed); locks[0].CloseAsync().Returns(LockitLockingState.Closed);
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -278,7 +278,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync().Returns(LockitLockingState.Closed); locks[0].CloseAsync().Returns(LockitLockingState.Closed);
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -347,7 +347,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync() locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => { throw new OutOfReachException(); }); .Returns<Task<LockitLockingState?>>(x => { throw new OutOfReachException(); });
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); bike.LockInfo.State.Returns(LockingState.Open);
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -421,7 +421,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync() locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => { throw new Exception("Exception message."); }); .Returns<Task<LockitLockingState?>>(x => { throw new Exception("Exception message."); });
bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); bike.LockInfo.State.Returns(LockingState.Open);
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -500,7 +500,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", response)); connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", response));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>()); locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
@ -571,7 +571,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info", new Exception("chub"))); connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;
@ -642,7 +642,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.", new Exception("chub"))); connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result; var subsequent = handler.HandleRequestOption1().Result;

View file

@ -312,6 +312,8 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableClosed( var handler = new DisposableClosed(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -374,6 +376,8 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableClosed( var handler = new DisposableClosed(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -449,6 +453,8 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableClosed( var handler = new DisposableClosed(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -525,6 +531,8 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableClosed( var handler = new DisposableClosed(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate
@ -600,6 +608,8 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.CopriLock.RequestHandler
var bikesViewModel = Substitute.For<IBikesViewModel>(); var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>(); var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableClosed( var handler = new DisposableClosed(
bike, bike,
() => true, // isConnectedDelegate () => true, // isConnectedDelegate

View file

@ -1040,7 +1040,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel
Assert.AreEqual("Available.", bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText); Assert.AreEqual("Available.", bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText);
Assert.AreEqual("Available.", bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText); Assert.AreEqual("Available.", bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText);
Assert.AreEqual("Max. reservation time of 15 min. expired.", bikesAtStation.FirstOrDefault(x => x.Id == "FR1004").StateText); Assert.AreEqual("Max. reservation time expired.", bikesAtStation.FirstOrDefault(x => x.Id == "FR1004").StateText);
Assert.AreEqual($"Rented since {DateTime.Parse("2021-11-06 18:57:25.445447+01"):dd. MMMM HH:mm}.", bikesAtStation.FirstOrDefault(x => x.Id == "FR1544").StateText); // Was 7 Assert.AreEqual($"Rented since {DateTime.Parse("2021-11-06 18:57:25.445447+01"):dd. MMMM HH:mm}.", bikesAtStation.FirstOrDefault(x => x.Id == "FR1544").StateText); // Was 7
// Login hint/ no bikes frame // Login hint/ no bikes frame

View file

@ -1,4 +1,4 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using TINK.ViewModel; using TINK.ViewModel;
@ -10,7 +10,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel
[Test] [Test]
public void TestGetReservedInfo() public void TestGetReservedInfo()
{ {
Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(null), Is.EqualTo("Max. reservation time of 15 min. expired.")); Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(null), Is.EqualTo("Max. reservation time expired."));
Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(null, code: "Code123"), Is.Not.Null); Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(null, code: "Code123"), Is.Not.Null);
Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(3), code: "Code123"), Is.Not.Null); Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(3), code: "Code123"), Is.Not.Null);
Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(60)), Is.EqualTo("Still 1 min. reserved.")); Assert.That(new BikeAtStationInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(60)), Is.EqualTo("Still 1 min. reserved."));

View file

@ -1,4 +1,4 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using TINK.ViewModel; using TINK.ViewModel;
@ -10,9 +10,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel
[Test] [Test]
public void TestGetReservedInfo() public void TestGetReservedInfo()
{ {
Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null), Is.EqualTo("Max. reservation time of 15 min. expired.")); Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null), Is.EqualTo("Max. reservation time expired."));
Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null, code: "Code12"), Is.Not.Null); Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null, code: "Code12"), Is.Not.Null);
Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null, "12"), Is.EqualTo("Location 12, max. reservation time of 15 min. expired.")); Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null, "12"), Is.EqualTo("Location 12, max. reservation time expired."));
Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null, "12", "Code12"), Is.Not.Null); Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(null, "12", "Code12"), Is.Not.Null);
Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(10)), Is.Not.Null); Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(10)), Is.Not.Null);
Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(10), "123"), Is.EqualTo("Location Station 123, still 0 min. reserved.")); Assert.That(new MyBikeInUseStateInfoProvider().GetReservedInfo(TimeSpan.FromSeconds(10), "123"), Is.EqualTo("Location Station 123, still 0 min. reserved."));

View file

@ -1,3 +1,4 @@
admin
app app
auth auth
backend backend
@ -5,19 +6,23 @@ bluetooth
bord bord
copri copri
deserialization deserialization
Deserialize
deserialized deserialized
deserializing
flyout flyout
Geolocation Geolocation
gps gps
Guid Guid
https https
Namespace Namespace
nullable
offline offline
ok ok
pedelecs pedelecs
popup popup
pwd pwd
refactored refactored
serializer
uri uri
uris uris
Url Url