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

@ -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
{