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
ui
xdoc
xml

View file

@ -1,5 +1,5 @@
<?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" />
<!-- Google Maps related permissions -->
<!-- Permission to receive remote notifications from Google Play Services -->

View file

@ -56,8 +56,8 @@
<key>CFBundleDisplayName</key>
<string>LastenradBayern</string>
<key>CFBundleVersion</key>
<string>365</string>
<string>366</string>
<key>CFBundleShortVersionString</key>
<string>3.0.365</string>
<string>3.0.366</string>
</dict>
</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"?>
<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" />
<!-- Google Maps related permissions -->
<!-- Permission to receive remote notifications from Google Play Services -->

View file

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

View file

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

View file

@ -1,5 +1,5 @@
<?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" />
<!-- Google Maps related permissions -->
<!-- Permission to receive remote notifications from Google Play Services -->

View file

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

View file

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

View file

@ -21,7 +21,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
private readonly StateInfoMutable _StateInfo;
/// <summary>
/// Constructs a bike.
/// Constructs a bike info object.
/// </summary>
/// <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>
@ -38,7 +38,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
string stationId = null,
string stationName = null,
Uri operatorUri = null,
RentalDescription tariffDescription = null,
IRentalDescription tariffDescription = null,
Func<DateTime> dateTimeProvider = null,
IStateInfo stateInfo = null)
{
@ -65,7 +65,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary> Holds description about the tariff. </summary>
[DataMember]
public RentalDescription TariffDescription { get; private set; }
public IRentalDescription TariffDescription { get; private set; }
/// <summary>
/// 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>
DataSource DataSource { get; set; }
/// <summary>
/// Gets or set the rental description.
/// </summary>
IRentalDescription TariffDescription { get; }
event PropertyChangedEventHandler PropertyChanged;
}

View file

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

View file

@ -84,8 +84,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
new StateInfo(
dateTimeProvider,
requestedAt,
tariffDescription?.MaxReservationTimeSpan ?? StateRequestedInfo.UNKNOWNMAXRESERVATIONTIMESPAN,
mailAddress,
""),
""), // BC code
bike != null
? new Bike(
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;
namespace TINK.Model.Bikes.BikeInfoNS
@ -6,20 +7,20 @@ namespace TINK.Model.Bikes.BikeInfoNS
/// Successor of TarifDescription- object.
/// Manages tariff- and rental info.
/// </summary>
public class RentalDescription
public class RentalDescription : IRentalDescription
{
/// <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>
public class TariffElement
{
/// <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>
public string Description { get; set; } = string.Empty;
/// <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>
public string Value { get; set; } = string.Empty;
}
@ -30,12 +31,12 @@ namespace TINK.Model.Bikes.BikeInfoNS
public class InfoElement
{
/// <summary>
/// Key which identyfies the value (required for special processing)
/// Key which identifies the value (required for special processing)
/// </summary>
public string Key { get; set; }
/// <summary>
/// Text to be displayed to user.
/// Text (language aware) to be displayed to user.
/// </summary>
public string Value { get; set; }
}
@ -50,8 +51,19 @@ namespace TINK.Model.Bikes.BikeInfoNS
/// </summary>
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>();
/// <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>();
}
}

View file

@ -438,5 +438,20 @@ namespace TINK.Model.Connector
name,
int.TryParse(bikeGroup?.bike_count ?? "0", out var countCity) ? countCity : 0,
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)
{
// 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}");
return null;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
using System;
using System;
namespace TINK.Model.State
{
@ -6,15 +6,17 @@ namespace TINK.Model.State
{
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="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="notifyLevel">Controls whether notify property changed events are fired or not.</param>
void Load(
InUseStateEnum state,
DateTime? from = null,
TimeSpan? reservationTimeSpan = null,
string mailAddress = null,
string code = null,
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.
/// </summary>
/// <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="code">Booking code.</param>
/// <param name="dateTimeNowProvider">Date time provider to calculate remaining time.</param>
public StateInfo(
Func<DateTime> dateTimeNowProvider,
DateTime requestedAt,
TimeSpan maxReservationTimeSpan,
string mailAddress,
string code)
{
// Todo: Handle p_oFrom == null here.
// Todo: Handle p_oDuration == null here.
_InUseState = new StateRequestedInfo(
dateTimeNowProvider ?? (() => DateTime.Now),
requestedAt,
maxReservationTimeSpan,
mailAddress,
code);
}
@ -81,9 +82,6 @@ namespace TINK.Model.State
string p_strMailAddress,
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(
p_oBookedAt,
p_strMailAddress,
@ -114,7 +112,7 @@ namespace TINK.Model.State
}
/// <summary>
/// Date of request/ bookeing action.
/// Date of request/ booking action.
/// </summary>
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>
/// Mail address.
/// </summary>
@ -152,7 +158,7 @@ namespace TINK.Model.State
/// <summary>
/// Tries update
/// </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>
public bool GetIsStillReserved(out TimeSpan? p_oRemainingTime)
{

View file

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

View file

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

View file

@ -692,6 +692,11 @@ namespace TINK.Model
AppResources.ChangeLog_3_0_365_MK_SB,
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>

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>
/// Looks up a localized string similar to We have fixed some bugs. Enjoy the ride!.
/// </summary>
@ -2103,7 +2112,16 @@ namespace TINK.MultilingualResources {
}
/// <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>
public static string MarkingFindBikeLabel {
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>
/// Looks up a localized string similar to Menu.
/// </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>
/// Looks up a localized string similar to Connection error when canceling the reservation!.
/// </summary>
@ -3039,7 +3084,7 @@ namespace TINK.MultilingualResources {
}
/// <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>
public static string PlaceholderFindBike {
get {
@ -3248,7 +3293,7 @@ namespace TINK.MultilingualResources {
}
/// <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>
public static string StatusTextReservationExpiredCodeLocationMaxReservationTime {
get {
@ -3266,7 +3311,7 @@ namespace TINK.MultilingualResources {
}
/// <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>
public static string StatusTextReservationExpiredCodeMaxReservationTime {
get {
@ -3284,7 +3329,7 @@ namespace TINK.MultilingualResources {
}
/// <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>
public static string StatusTextReservationExpiredLocationMaxReservationTime {
get {
@ -3302,7 +3347,7 @@ namespace TINK.MultilingualResources {
}
/// <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>
public static string StatusTextReservationExpiredMaximumReservationTime {
get {

View file

@ -182,7 +182,7 @@ Die Protokolldatei enthält Ihre App-Nutzungsdaten sowie Systeminformationen. Di
Bitte melden Sie sich erneut an.</value>
</data>
<data name="StatusTextReservationExpiredCodeMaxReservationTime" xml:space="preserve">
<value>Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen.
<value>Code ist {0}, max. Reservierungszeit abgelaufen.
</value>
</data>
<data name="StatusTextReservationExpiredCodeRemaining" xml:space="preserve">
@ -199,13 +199,13 @@ Bitte melden Sie sich erneut an.</value>
<value>Noch {0} Min. reserviert.</value>
</data>
<data name="StatusTextReservationExpiredMaximumReservationTime" xml:space="preserve">
<value>Max. Reservierungszeit von {0} Min. abgelaufen.</value>
<value>Max. Reservierungszeit abgelaufen.</value>
</data>
<data name="StatusTextBookedCodeSince" xml:space="preserve">
<value>Code ist {0}, gemietet seit {1}.</value>
</data>
<data name="StatusTextReservationExpiredLocationMaxReservationTime" xml:space="preserve">
<value>Standort {0}, max. Reservierungszeit von {1} Min. abgelaufen.
<value>Standort {0}, max. Reservierungszeit abgelaufen.
</value>
</data>
<data name="StatusTextAvailable" xml:space="preserve">
@ -216,7 +216,7 @@ Bitte melden Sie sich erneut an.</value>
</value>
</data>
<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>
</data>
<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>
</data>
<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 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!
@ -1171,7 +1171,7 @@ Außerdem:&lt;br/&gt;
- Paketaktualisierungen</value>
</data>
<data name="MarkingFindBikeLabel" xml:space="preserve">
<value>Rad-ID inklusive Präfix</value>
<value>Rad-ID</value>
</data>
<data name="MarkingSearchBike" xml:space="preserve">
<value>Rad suchen</value>
@ -1188,4 +1188,19 @@ Außerdem:&lt;br/&gt;
<data name="ChangeLog_3_0_365_MK_SB" xml:space="preserve">
<value>Kleine Verbesserungen in Design und Performance.</value>
</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>

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>
</data>
<data name="StatusTextReservationExpiredMaximumReservationTime" xml:space="preserve">
<value>Max. reservation time of {0} min. expired.</value>
<value>Max. reservation time expired.</value>
</data>
<data name="StatusTextReservationExpiredCodeRemaining" xml:space="preserve">
<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>
</data>
<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 name="StatusTextAvailable" xml:space="preserve">
<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>
</data>
<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 name="StatusTextReservationExpiredCodeLocationReservationTime" xml:space="preserve">
<value>Code {0}, location {1}, still {2} min. reserved.</value>
</data>
<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 name="StatusTextReservationExpiredLocationReservationTime" xml:space="preserve">
<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>
</data>
<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 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!
@ -1260,7 +1260,7 @@ Also:&lt;br/&gt;
- Package updates</value>
</data>
<data name="MarkingFindBikeLabel" xml:space="preserve">
<value>Bike ID including prefix</value>
<value>Bike id</value>
</data>
<data name="MarkingSearchBike" xml:space="preserve">
<value>Search bike</value>
@ -1277,4 +1277,19 @@ Also:&lt;br/&gt;
<data name="ChangeLog_3_0_365_MK_SB" xml:space="preserve">
<value>Minor design and performance improvements.</value>
</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>

View file

@ -237,8 +237,8 @@ Please log in again.</source>
Bitte melden Sie sich erneut an.</target>
</trans-unit>
<trans-unit id="StatusTextReservationExpiredCodeMaxReservationTime" translate="yes" xml:space="preserve">
<source>Code {0}, max. reservation time of {1} min. expired.</source>
<target state="translated">Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen.
<source>Code {0}, max. reservation time expired.</source>
<target state="translated">Code ist {0}, max. Reservierungszeit abgelaufen.
</target>
</trans-unit>
<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>
</trans-unit>
<trans-unit id="StatusTextReservationExpiredMaximumReservationTime" translate="yes" xml:space="preserve">
<source>Max. reservation time of {0} min. expired.</source>
<target state="translated">Max. Reservierungszeit von {0} Min. abgelaufen.</target>
<source>Max. reservation time expired.</source>
<target state="translated">Max. Reservierungszeit abgelaufen.</target>
</trans-unit>
<trans-unit id="StatusTextBookedCodeSince" translate="yes" xml:space="preserve">
<source>Code {0}, rented since {1}.</source>
<target state="translated">Code ist {0}, gemietet seit {1}.</target>
</trans-unit>
<trans-unit id="StatusTextReservationExpiredLocationMaxReservationTime" translate="yes" xml:space="preserve">
<source>Location {0}, max. reservation time of {1} min. expired.</source>
<target state="translated">Standort {0}, max. Reservierungszeit von {1} Min. abgelaufen.
<source>Location {0}, max. reservation time expired.</source>
<target state="translated">Standort {0}, max. Reservierungszeit abgelaufen.
</target>
</trans-unit>
<trans-unit id="StatusTextAvailable" translate="yes" xml:space="preserve">
@ -281,8 +281,8 @@ Bitte melden Sie sich erneut an.</target>
</target>
</trans-unit>
<trans-unit id="StatusTextReservationExpiredCodeLocationMaxReservationTime" translate="yes" xml:space="preserve">
<source>Code {0}, location {1}, max. reservation time of {2} min. expired.</source>
<target state="translated">Code ist {0}, Standort {1}, max. Reservierungszeit von {2} Min. abgelaufen.
<source>Code {0}, location {1}, max. reservation time expired.</source>
<target state="translated">Code ist {0}, Standort {1}, max. Reservierungszeit abgelaufen.
</target>
</trans-unit>
<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>
</trans-unit>
<trans-unit id="PlaceholderFindBike" translate="yes" xml:space="preserve">
<source>Bike ID including prefix e.g. TR15</source>
<target state="translated">Rad-ID inklusive Präfix, z.B. TR15</target>
<source>Prefix and No., e.g. TR15</source>
<target state="translated">Präfix und Nr., z.B. TR15</target>
</trans-unit>
<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!
@ -1620,8 +1620,8 @@ Außerdem:&lt;br/&gt;
- Paketaktualisierungen</target>
</trans-unit>
<trans-unit id="MarkingFindBikeLabel" translate="yes" xml:space="preserve">
<source>Bike ID including prefix</source>
<target state="translated">Rad-ID inklusive Präfix</target>
<source>Bike id</source>
<target state="translated">Rad-ID</target>
</trans-unit>
<trans-unit id="MarkingSearchBike" translate="yes" xml:space="preserve">
<source>Search bike</source>
@ -1643,6 +1643,26 @@ Außerdem:&lt;br/&gt;
<source>Minor design and performance improvements.</source>
<target state="translated">Kleine Verbesserungen in Design und Performance.</target>
</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>
</body>
</file>

View file

@ -550,7 +550,7 @@ namespace TINK.Repository
#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="command">Command to log user in.</param>
/// <returns>Response on cancel booking request.</returns>

View file

@ -1376,7 +1376,7 @@ namespace TINK.Repository
}
/// <summary>
/// Gets canel booking request response.
/// Gets cancel booking request response.
/// </summary>
/// <param name="bikeId">Id of the bike to book.</param>
/// <param name="cookie">Cookie of the logged in user.</param>
@ -1613,7 +1613,7 @@ namespace TINK.Repository
}
/// <summary>
/// Gets canel booking request response.
/// Gets cancel booking request response.
/// </summary>
/// <param name="bikeId">Id of the bike to book.</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>
/// 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
/// - 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?
/// </summary>
public class AuthcookieNotDefinedException : InvalidResponseException<Response.ResponseBase>
@ -19,7 +19,7 @@
/// Gets whether authcookie is defined or not.
/// </summary>
/// <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>
/// <returns></returns>
public static bool IsAuthcookieNotDefined(
@ -29,7 +29,7 @@
{
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;
return false;
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace TINK.Repository.Response
@ -22,6 +22,12 @@ namespace TINK.Repository.Response
[DataMember]
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>
[DataMember]
public Dictionary<

View file

@ -115,7 +115,7 @@ namespace TINK.Services.CopriApi
throw new BikeStillInStationException("Booking was canceled because bike is still in station.");
}
// Upate booking state.
// Update booking state.
bike.Load(
response,
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
=> RequestHandler.IsButtonVisible
&& 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.Device;
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; }
/// <summary>
/// Constructs the reqest handler base.
/// Constructs the request handler base.
/// </summary>
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param>
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...).</param>

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BC;
@ -55,7 +55,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
/// <summary>Gets the is connected state. </summary>
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;
/// <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(
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.MessageAnswerNo);

View file

@ -177,14 +177,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
RaisePropertyChangedEvent(lastHandler);
}
/// <summary> Gets visiblity of the copri command button. </summary>
/// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible;
/// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText;
/// <summary> Gets visiblity of the ILockIt command button. </summary>
/// <summary> Gets visibility of the ILockIt command button. </summary>
public bool 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>
{
/// <summary>
/// Constructs the reqest handler base.
/// Constructs the request handler base.
/// </summary>
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param>
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>

View file

@ -62,7 +62,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Ask whether to really reserve bike?
var alertResult = await ViewService.DisplayAlert(
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.MessageAnswerNo);

View file

@ -1,10 +1,10 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
{
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; }
/// <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>
/// Performs the copri action to be executed when user presses the copri button managed by request handler.
/// </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> HandleRequestOption2();
/// <summary>
/// Holds error discription (invalid state).
/// Holds error description (invalid state).
/// </summary>
string ErrorText { get; }
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
@ -36,7 +36,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
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 string ErrorText { get; }

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Serilog;
using TINK.Model.State;
@ -38,7 +38,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
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 async Task<IRequestHandler> HandleRequestOption1()

View file

@ -136,7 +136,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
RaisePropertyChangedEvent(lastHandler);
}
/// <summary> Gets visiblity of the copri command button. </summary>
/// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible
&& 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>
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
=> RequestHandler.IsLockitButtonVisible
&& 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.Device;
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>
{
/// <summary>
/// Constructs the reqest handler base.
/// Constructs the request handler base.
/// </summary>
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param>
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>

View file

@ -132,7 +132,10 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Ask whether to really reserve bike?
var alertResult = await ViewService.DisplayAlert(
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.MessageAnswerNo);

View file

@ -1,4 +1,4 @@
using System;
using System;
using Serilog;
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
using TINK.Model.Connector;
@ -54,7 +54,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
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(
selectedCopriLock,
isConnectedDelegate,
@ -79,7 +79,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
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;
switch (lockState)
{

View file

@ -1,4 +1,4 @@
namespace TINK.ViewModel.Bikes.Bike
namespace TINK.ViewModel.Bikes.Bike
{
/// <summary>
/// Base interface for Copri and ILockIt request handler.
@ -22,7 +22,7 @@
/// <summary>Gets the is connected state. </summary>
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; }
}
}

View file

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

View file

@ -1,5 +1,4 @@
using System;
using TINK.Model.State;
using System;
using TINK.MultilingualResources;
using TINK.ViewModel.Bikes.Bike;
@ -8,7 +7,7 @@ namespace TINK.ViewModel
public class BikeAtStationInUseStateInfoProvider : IInUseStateInfoProvider
{
/// <summary> Gets reserved into display text. </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetReservedInfo(
TimeSpan? remainingTime,
@ -21,10 +20,10 @@ namespace TINK.ViewModel
if (string.IsNullOrEmpty(code))
{
// 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))
@ -36,7 +35,7 @@ namespace TINK.ViewModel
}
/// <summary> Gets booked into display text. </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetBookedInfo(
DateTime? from,

View file

@ -10,10 +10,13 @@ using Plugin.BLE.Abstractions.Contracts;
using Serilog;
using TINK.Model;
using TINK.Model.Bikes;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
using TINK.Model.Connector;
using TINK.Model.Connector.Filter;
using TINK.Model.Device;
using TINK.Model.Services.CopriApi;
using TINK.Model.State;
using TINK.Model.Stations.StationNS;
using TINK.Model.User;
using TINK.MultilingualResources;
@ -25,6 +28,7 @@ using TINK.Services.Permissions;
using TINK.Settings;
using TINK.View;
using TINK.ViewModel.Bikes;
using TINK.ViewModel.Map;
using Xamarin.Essentials;
using Xamarin.Forms;
using Command = Xamarin.Forms.Command;
@ -84,13 +88,13 @@ namespace TINK.ViewModel.FindBike
/// <summary>
/// True if ListView of Bikes is refreshing after user pulled;
/// </summary>
private bool _isRefreshing = false;
private bool isRefreshing = false;
public bool IsRefreshing
{
get { return _isRefreshing; }
get { return isRefreshing; }
set
{
_isRefreshing = value;
isRefreshing = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing)));
}
}
@ -99,6 +103,7 @@ namespace TINK.ViewModel.FindBike
/// Holds what should be executed on pull to refresh
/// </summary>
public Command RefreshCommand { get; }
public Command ShowFilterBikeTypeInfoCommand { get; private set; }
/// <summary>
/// Constructs bike collection view model in case information about occupied bikes is available.
@ -146,6 +151,15 @@ namespace TINK.ViewModel.FindBike
await SelectBike();
});
ShowFilterBikeTypeInfoCommand = new Xamarin.Forms.Command(async () => {
await ViewService.DisplayAlert(
AppResources.MessageBikeTypeInfoTitle,
AppResources.MessageBikeTypeInfoText,
AppResources.MessageAnswerOk);
});
}
/// <summary>
@ -158,6 +172,8 @@ namespace TINK.ViewModel.FindBike
Log.ForContext<FindBikePageViewModel>().Information("User request to show page FindBike- page re-appearing");
ActiveFilteredBikeType = string.Empty;
IsConnected = IsConnectedDelegate();
// Stop polling before getting bikes info.
@ -185,6 +201,19 @@ namespace TINK.ViewModel.FindBike
ActionText = string.Empty;
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>
@ -209,7 +238,7 @@ namespace TINK.ViewModel.FindBike
if (exception is WebConnectFailureException)
{
// 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(
AppResources.ErrorReturnBikeNoWebTitle,
@ -402,6 +431,9 @@ namespace TINK.ViewModel.FindBike
IsIdle = true;
return;
}
BikeIdUserInput = string.Empty;
}
/// <summary> Create task which updates my bike view model.</summary>
@ -417,13 +449,12 @@ namespace TINK.ViewModel.FindBike
null);
var result = ConnectorFactory(IsConnected).Query.GetBikesAsync().Result;
var bikes = result.Response;
var exception = result.Exception;
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));
@ -441,5 +472,44 @@ namespace TINK.ViewModel.FindBike
},
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
{

View file

@ -50,7 +50,6 @@ namespace TINK.ViewModel.Map
/// </summary>
private Exception m_oException;
/// <summary>
/// Service to query/ manage permissions (location) of the app.
/// </summary>
@ -483,10 +482,23 @@ namespace TINK.ViewModel.Map
Log.ForContext<MapPageViewModel>().Verbose("Location permissions: {0}.", status);
}
private bool isLocationPermissionGranted = false;
/// <summary>
/// Exposes IsLocationPermissionGranted.
/// </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>
/// 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);
}
// Get and expose status of location permission
GetLocationPermissionStatus();
// Load MyBikes Count -> MyBikes Icon/Button
GetMyBikesCount(resultStationsAndBikes.Response.BikesOccupied);

View file

@ -8,7 +8,7 @@ namespace TINK.ViewModel
public class MyBikeInUseStateInfoProvider : IInUseStateInfoProvider
{
/// <summary> Gets reserved into display text. </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetReservedInfo(
TimeSpan? remainingTime,
@ -17,25 +17,25 @@ namespace TINK.ViewModel
{
if (remainingTime == null)
{
// Reamining time is not available.
// Remaining time is not available.
if (stationId == null)
{
if (string.IsNullOrEmpty(code))
{
// 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))
{
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))
@ -63,7 +63,7 @@ namespace TINK.ViewModel
/// <summary>
/// Gets booked into display text.
/// </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetBookedInfo(
DateTime? from,

View file

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

View file

@ -45,7 +45,7 @@ namespace UITest.Fixtures.ViewModel
}
}
/// <summary>
/// Tests base class functionaltiy by using child.
/// Tests base class functionality by using child.
/// </summary>
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(
InUseStateEnum.Reserved, // Copri acknowledges state reserved.
new DateTime(1980, 1, 1), // Date when bike was booked.
TimeSpan.FromMinutes(15),
"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" }));
@ -87,7 +88,7 @@ namespace UITest.Fixtures.ViewModel
}
/// <summary>
/// Tests base class functionaltiy by using child.
/// Tests base class functionality by using child.
/// </summary>
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(
InUseStateEnum.Reserved, // Copri acknowledges state reserved.
new DateTime(1980, 1, 1),
TimeSpan.FromMinutes(15),
"ragu@gnu-systems.de", // Owner from Copri.
"4asdfA"); // Reservation code from Copri
@ -129,7 +131,7 @@ namespace UITest.Fixtures.ViewModel
}
/// <summary>
/// Tests base class functionaltiy by using child.
/// Tests base class functionality by using child.
/// </summary>
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(
InUseStateEnum.Booked,
new DateTime(2017, 10, 24, 21, 49, 3),
TimeSpan.FromMinutes(15),
"ragu@gnu-systems.de",
"4asdfA");

View file

@ -65,7 +65,7 @@ namespace TestTINKLib
public void TestUpdate_Null()
{
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
{
@ -93,7 +93,7 @@ namespace TestTINKLib
public void TestUpdate()
{
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
{

View file

@ -322,5 +322,15 @@ namespace TestShareeLib.Model.Connector
Is.EqualTo(TINK.Model.State.InUseStateEnum.FeedbackPending),
"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(),
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 TINK.Model.Connector.Updater;
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.MailAddress.Returns("who@the");
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);
@ -85,6 +86,7 @@ namespace TestTINKLib
l_oStateInfo.Load(
InUseStateEnum.Reserved, // Copri acknowledges state reserved.
l_oDateTimeMock.GetDateTime(),
TimeSpan.FromMinutes(15),
"heiz@mustermann"); // Owner from Copri.
// Invoke first update (after simulated 4mns)
@ -110,7 +112,7 @@ namespace TestTINKLib
Func<DateTime> l_oNowMock = () => new DateTime(2017, 09, 21, 23, 40, 0);
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("Booked", l_oStateInfo.ToString());
@ -120,7 +122,7 @@ namespace TestTINKLib
Assert.AreEqual("21", l_oStateInfo.Code);
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.
Assert.AreEqual(InUseStateEnum.Reserved, l_oStateInfo.Value);
@ -130,7 +132,7 @@ namespace TestTINKLib
Assert.AreEqual("heiz@mustermann", l_oStateInfo.MailAddress);
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.
Assert.AreEqual(InUseStateEnum.Disposable, l_oStateInfo.Value);
@ -156,6 +158,7 @@ namespace TestTINKLib
var l_oSource = new StateInfo(
() => new DateTime(2018, 01, 03, 21, 53, 0),
new DateTime(2018, 01, 03, 21, 13, 0), // Requested from
TimeSpan.FromMinutes(15),
"a@b",
"123");
l_oState.Load(l_oSource);
@ -184,5 +187,51 @@ namespace TestTINKLib
Assert.IsNull(l_oState.MailAddress);
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()
{
// Create object to test.
var l_oInUseState = new StateInfoMutable(
() => new DateTime(2017, 11, 18, 23, 20, 0) // Mocked time stamp returned when StateInfo- object is crated
var l_oInUseState = new StateInfoMutable(() => new DateTime(2017, 11, 18, 23, 20, 0) // Mocked time stamp returned when StateInfo- object is crated
);
l_oInUseState.Load(
InUseStateEnum.Booked,
new DateTime(2017, 11, 18, 23, 19, 0), // Time booked at
TimeSpan.FromMinutes(15),
"heiz@mustermann",
"173"); // Code
@ -111,6 +111,7 @@ namespace TestTINKLib.Fixtures.State
l_oInUseState.Load(
InUseStateEnum.Reserved,
new DateTime(2017, 09, 21, 23, 20, 0),
TimeSpan.FromMinutes(15),
"heiz@mustermann");
Assert.AreEqual(InUseStateEnum.Reserved, l_oInUseState.Value);
@ -167,6 +168,7 @@ namespace TestTINKLib.Fixtures.State
l_oInUseState.Load(
InUseStateEnum.Reserved,
l_oFrom,
TimeSpan.FromMinutes(15),
"z@C",
"01815A");

View file

@ -15,25 +15,25 @@ namespace TestTINKLib
{
Assert.AreEqual(
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(
"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(
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(
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(
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(
14,
remainingTime.Value.Minutes);
l_oRemainigTime.Value.Minutes);
}
[Test]
@ -41,26 +41,26 @@ namespace TestTINKLib
{
Assert.AreEqual(
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(
"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(
"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(
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(
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(
3,
remainingTime.Value.Minutes);
l_oRemainigTime.Value.Minutes);
}
[Test]
@ -72,7 +72,7 @@ namespace TestTINKLib
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(new DateTime(2017, 09, 20, 22, 01, 00), l_oReservedInfo.From, "a@b");
@ -86,5 +86,39 @@ namespace TestTINKLib
Assert.IsNull(remainingTime);
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(
() => new DateTime(2017, 09, 20),
new DateTime(2017, 09, 19),
TimeSpan.FromMinutes(15),
"ä@b",
"372");

View file

@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using NUnit.Framework;
using TINK.Repository.Response;
@ -129,7 +129,7 @@ namespace TestShareeLib.Repository.Response
}
/// <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>
[Test]
public void TestDeserialize_NewElement()
@ -186,7 +186,47 @@ namespace TestShareeLib.Repository.Response
Assert.That(
tariffDescription.tarif_elements.Count,
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;
namespace TestShareeLib.Repository.Response
@ -73,7 +73,7 @@ namespace TestShareeLib.Repository.Response
}
/// <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>
[Test]
public void TestDeserialize_NewElement()

View file

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

View file

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

View file

@ -182,7 +182,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
/// <summary>
/// Use case: End rental.
/// Comment: User deceide to abort returning bike
/// Comment: User decide to abort returning bike
/// Final state: Same as initial state.
/// </summary>
[Test]
@ -343,7 +343,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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.
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")));
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.
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 =>
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.
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 =>
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.
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 =>
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.
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."));
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.
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 activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => true, // isConnectedDelegate
@ -115,6 +117,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => true, // isConnectedDelegate
@ -194,6 +198,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => 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
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.
var subsequent = handler.HandleRequestOption1().Result;
@ -257,6 +263,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => 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")));
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.
var subsequent = handler.HandleRequestOption1().Result;
@ -320,6 +328,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var bikesViewModel = Substitute.For<IBikesViewModel>();
var activeUser = Substitute.For<IUser>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => 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."));
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.
var subsequent = handler.HandleRequestOption1().Result;
@ -384,6 +394,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => 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.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.
var subsequent = handler.HandleRequestOption1().Result;
@ -450,6 +462,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => 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.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.
var subsequent = handler.HandleRequestOption1().Result;
@ -516,6 +530,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
var activeUser = Substitute.For<IUser>();
var timeOuts = Substitute.For<ITimeOutProvider>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => true, // isConnectedDelegate
@ -534,7 +550,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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.
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));
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;
@ -141,7 +141,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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);
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."));
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);
var subsequent = handler.HandleRequestOption1().Result;

View file

@ -52,7 +52,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
/// <summary>
/// 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.
/// </summary>
[Test]
@ -135,7 +135,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
bike.Id.Returns("0");
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.
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));
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.
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));
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.
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));
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.
var subsequent = handler.HandleRequestOption1().Result;

View file

@ -50,7 +50,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
/// <summary>
/// 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.
/// </summary>
[Test]
@ -131,7 +131,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bike.Id.Returns("0");
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.
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));
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.
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));
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.
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));
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.
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));
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.
var subsequent = handler.HandleRequestOption1().Result;
@ -143,7 +143,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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;
@ -212,7 +212,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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;
@ -278,7 +278,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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;
@ -347,7 +347,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync()
.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);
var subsequent = handler.HandleRequestOption1().Result;
@ -421,7 +421,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync()
.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);
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));
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>());
@ -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")));
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;
@ -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")));
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;

View file

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

View file

@ -1,4 +1,4 @@
using System;
using System;
using NUnit.Framework;
using TINK.ViewModel;
@ -10,7 +10,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel
[Test]
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(TimeSpan.FromSeconds(3), code: "Code123"), Is.Not.Null);
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 TINK.ViewModel;
@ -10,9 +10,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel
[Test]
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, "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(TimeSpan.FromSeconds(10)), Is.Not.Null);
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
auth
backend
@ -5,19 +6,23 @@ bluetooth
bord
copri
deserialization
Deserialize
deserialized
deserializing
flyout
Geolocation
gps
Guid
https
Namespace
nullable
offline
ok
pedelecs
popup
pwd
refactored
serializer
uri
uris
Url