mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-04-21 12:36:28 +02:00
Unused files removed and differences updated.
This commit is contained in:
parent
9793a71d6f
commit
af3c20ea1c
129 changed files with 22 additions and 44291 deletions
|
@ -1,589 +0,0 @@
|
|||
using System;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Repository.Response;
|
||||
using TINK.Model.User.Account;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.State;
|
||||
using TINK.Repository.Exception;
|
||||
using Serilog;
|
||||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.Bike.BC.IBikeInfoMutable;
|
||||
using System.Globalization;
|
||||
using TINK.Model.Station.Operator;
|
||||
using Xamarin.Forms;
|
||||
using System.Linq;
|
||||
using TINK.Model.MiniSurvey;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects TINK app to copri using JSON as input data format.
|
||||
/// </summary>
|
||||
/// <todo>Rename to UpdateFromCopri.</todo>
|
||||
public static class UpdaterJSON
|
||||
{
|
||||
/// <summary> Loads a bike object from copri server cancel reservation/ booking update request.</summary>
|
||||
/// <param name="bike">Bike object to load response into.</param>
|
||||
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
||||
public static void Load(
|
||||
this IBikeInfoMutable bike,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel)
|
||||
{
|
||||
|
||||
bike.State.Load(InUseStateEnum.Disposable, notifyLevel: notifyLevel);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets all statsion for station provider and add them into station list.
|
||||
/// </summary>
|
||||
/// <param name="p_oStationList">List of stations to update.</param>
|
||||
public static StationDictionary GetStationsAllMutable(this StationsAvailableResponse stationsAllResponse)
|
||||
{
|
||||
// Get stations from Copri/ file/ memory, ....
|
||||
if (stationsAllResponse == null
|
||||
|| stationsAllResponse.stations == null)
|
||||
{
|
||||
// Latest list of stations could not be retrieved from provider.
|
||||
return new StationDictionary();
|
||||
}
|
||||
|
||||
Version.TryParse(stationsAllResponse.copri_version, out Version copriVersion);
|
||||
|
||||
var stations = new StationDictionary(p_oVersion: copriVersion);
|
||||
|
||||
foreach (var station in stationsAllResponse.stations)
|
||||
{
|
||||
if (stations.GetById(station.Value.station) != null)
|
||||
{
|
||||
// Can not add station to list of station. Id is not unique.
|
||||
throw new InvalidResponseException<StationsAvailableResponse>(
|
||||
string.Format("Station id {0} is not unique.", station.Value.station), stationsAllResponse);
|
||||
}
|
||||
|
||||
stations.Add(new Station.Station(
|
||||
station.Value.station,
|
||||
station.Value.GetGroup(),
|
||||
station.Value.GetPosition(),
|
||||
station.Value.description,
|
||||
new Data(station.Value.operator_data?.operator_name,
|
||||
station.Value.operator_data?.operator_phone,
|
||||
station.Value.operator_data?.operator_hours,
|
||||
station.Value.operator_data?.operator_email,
|
||||
!string.IsNullOrEmpty(station.Value.operator_data?.operator_color)
|
||||
? Color.FromHex(station.Value.operator_data?.operator_color)
|
||||
: (Color?)null)));
|
||||
}
|
||||
|
||||
return stations;
|
||||
}
|
||||
|
||||
/// <summary> Gets account object from login response.</summary>
|
||||
/// <param name="merchantId">Needed to extract cookie from autorization response.</param>
|
||||
/// <param name="loginResponse">Response to get session cookie and debug level from.</param>
|
||||
/// <param name="mail">Mail address needed to construct a complete account object (is not part of response).</param>
|
||||
/// <param name="password">Password needed to construct a complete account object (is not part of response).</param>
|
||||
public static IAccount GetAccount(
|
||||
this AuthorizationResponse loginResponse,
|
||||
string merchantId,
|
||||
string mail,
|
||||
string password)
|
||||
{
|
||||
if (loginResponse == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loginResponse));
|
||||
}
|
||||
|
||||
return new Account(
|
||||
mail,
|
||||
password,
|
||||
loginResponse.authcookie?.Replace(merchantId, ""),
|
||||
loginResponse.GetGroup(),
|
||||
loginResponse.debuglevel == 1
|
||||
? Permissions.All :
|
||||
(Permissions)loginResponse.debuglevel) ;
|
||||
}
|
||||
|
||||
/// <summary> Load bike object from booking response. </summary>
|
||||
/// <param name="bike">Bike object to load from response.</param>
|
||||
/// <param name="bikeInfo">Booking response.</param>
|
||||
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
||||
/// <param name="p_strSessionCookie">Session cookie of user which books bike.</param>
|
||||
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
||||
public static void Load(
|
||||
this IBikeInfoMutable bike,
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.Bike.BC.NotifyPropertyChangedLevel.All)
|
||||
{
|
||||
|
||||
var l_oDateTimeProvider = dateTimeProvider != null
|
||||
? dateTimeProvider
|
||||
: () => DateTime.Now;
|
||||
|
||||
if (bike is Bike.BluetoothLock.BikeInfoMutable btBikeInfo)
|
||||
{
|
||||
btBikeInfo.LockInfo.Load(
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey());
|
||||
}
|
||||
|
||||
var l_oState = bikeInfo.GetState();
|
||||
switch (l_oState)
|
||||
{
|
||||
case InUseStateEnum.Disposable:
|
||||
bike.State.Load(
|
||||
InUseStateEnum.Disposable,
|
||||
notifyLevel: notifyLevel);
|
||||
break;
|
||||
|
||||
case InUseStateEnum.Reserved:
|
||||
bike.State.Load(
|
||||
InUseStateEnum.Reserved,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
notifyLevel);
|
||||
break;
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
bike.State.Load(
|
||||
InUseStateEnum.Booked,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
notifyLevel);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception(string.Format("Unexpected bike state detected. state is {0}.", l_oState));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes available from copri server response.</summary>
|
||||
/// <param name="bikesAvailableResponse">Response to create collection from.</param>
|
||||
/// <returns>New collection of available bikes.</returns>
|
||||
public static BikeCollection GetBikesAvailable(
|
||||
this BikesAvailableResponse bikesAvailableResponse)
|
||||
{
|
||||
return GetBikesAll(
|
||||
bikesAvailableResponse,
|
||||
new BikesReservedOccupiedResponse(), // There are no occupied bikes.
|
||||
string.Empty,
|
||||
() => DateTime.Now);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied from copri server response. </summary>
|
||||
/// <param name="p_oBikesAvailable">Response to create bikes from.</param>
|
||||
/// <returns>New collection of occupied bikes.</returns>
|
||||
public static BikeCollection GetBikesOccupied(
|
||||
this BikesReservedOccupiedResponse bikesOccupiedResponse,
|
||||
string mail,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
return GetBikesAll(
|
||||
new BikesAvailableResponse(),
|
||||
bikesOccupiedResponse,
|
||||
mail,
|
||||
dateTimeProvider);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied from copri server response. </summary>
|
||||
/// <param name="p_oBikesAvailable">Response to create bikes from.</param>
|
||||
/// <returns>New collection of occupied bikes.</returns>
|
||||
public static BikeCollection GetBikesAll(
|
||||
BikesAvailableResponse bikesAvailableResponse,
|
||||
BikesReservedOccupiedResponse bikesOccupiedResponse,
|
||||
string mail,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
var bikesDictionary = new Dictionary<string, BikeInfo>();
|
||||
var duplicates = new Dictionary<string, BikeInfo>();
|
||||
|
||||
// Get bikes from Copri/ file/ memory, ....
|
||||
if (bikesAvailableResponse != null
|
||||
&& bikesAvailableResponse.bikes != null)
|
||||
{
|
||||
foreach (var bikeInfoResponse in bikesAvailableResponse.bikes.Values)
|
||||
{
|
||||
var bikeInfo = BikeInfoFactory.Create(bikeInfoResponse);
|
||||
if (bikeInfo == null)
|
||||
{
|
||||
// Response is not valid.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bikesDictionary.ContainsKey(bikeInfo.Id))
|
||||
{
|
||||
// Duplicates are not allowed.
|
||||
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes available. Bike status is {bikeInfo.State.Value}.");
|
||||
|
||||
if (!duplicates.ContainsKey(bikeInfo.Id))
|
||||
{
|
||||
duplicates.Add(bikeInfo.Id, bikeInfo);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Get bikes from Copri/ file/ memory, ....
|
||||
if (bikesOccupiedResponse != null
|
||||
&& bikesOccupiedResponse.bikes_occupied != null)
|
||||
{
|
||||
foreach (var bikeInfoResponse in bikesOccupiedResponse.bikes_occupied.Values)
|
||||
{
|
||||
BikeInfo bikeInfo = BikeInfoFactory.Create(
|
||||
bikeInfoResponse,
|
||||
mail,
|
||||
dateTimeProvider);
|
||||
|
||||
if (bikeInfo == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bikesDictionary.ContainsKey(bikeInfo.Id))
|
||||
{
|
||||
// Duplicates are not allowed.
|
||||
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes occupied. Bike status is {bikeInfo.State.Value}.");
|
||||
if (!duplicates.ContainsKey(bikeInfo.Id))
|
||||
{
|
||||
duplicates.Add(bikeInfo.Id, bikeInfo);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove entries which are not unique.
|
||||
foreach (var l_oDuplicate in duplicates)
|
||||
{
|
||||
bikesDictionary.Remove(l_oDuplicate.Key);
|
||||
}
|
||||
|
||||
return new BikeCollection(bikesDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs bike info instances/ bike info derived instances.
|
||||
/// </summary>
|
||||
public static class BikeInfoFactory
|
||||
{
|
||||
public static BikeInfo Create(BikeInfoAvailable bikeInfo)
|
||||
{
|
||||
if (bikeInfo.GetIsManualLockBike())
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
$"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " +
|
||||
"Manual lock bikes are no more supported." +
|
||||
$"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $"station number {bikeInfo.station}" : string.Empty)}."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (bikeInfo.GetState())
|
||||
{
|
||||
case InUseStateEnum.Disposable:
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(bikeInfo.station))
|
||||
{
|
||||
// Bike available must always have a station id because bikes can only be returned at a station.
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. No station info set.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return !bikeInfo.GetIsBluetoothLockBike()
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription) null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Invalid response detected. Available bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Creates a bike info object from copri response. </summary>
|
||||
/// <param name="bikeInfo">Copri response. </param>
|
||||
/// <param name="mailAddress">Mail address of user.</param>
|
||||
/// <param name="dateTimeProvider">Date and time provider function.</param>
|
||||
/// <returns></returns>
|
||||
public static BikeInfo Create(
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
if (bikeInfo.GetIsManualLockBike())
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
$"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " +
|
||||
"Manual lock bikes are no more supported." +
|
||||
$"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $", station number {bikeInfo.station}" : string.Empty)}."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if bike is a bluetooth lock bike.
|
||||
var isBluetoothBike = bikeInfo.GetIsBluetoothLockBike();
|
||||
int lockSerial = bikeInfo.GetBluetoothLockId();
|
||||
Guid lockGuid = bikeInfo.GetBluetoothLockGuid();
|
||||
|
||||
switch (bikeInfo.GetState())
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
try
|
||||
{
|
||||
return !isBluetoothBike
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
dateTimeProvider)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
lockGuid,
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Reserved bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
try
|
||||
{
|
||||
return !isBluetoothBike
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (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;
|
||||
}
|
||||
|
||||
default:
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Bikes.Bike.TariffDescription Create(this TariffDescription tariffDesciption)
|
||||
{
|
||||
return new Bikes.Bike.TariffDescription
|
||||
{
|
||||
Name = tariffDesciption?.name,
|
||||
#if USCSHARP9
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null,
|
||||
#else
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?) null,
|
||||
#endif
|
||||
FreeTimePerSession = double.TryParse(tariffDesciption?.free_hours, NumberStyles.Any, CultureInfo.InvariantCulture, out double freeHours) ? TimeSpan.FromHours(freeHours) : TimeSpan.Zero,
|
||||
FeeEuroPerHour = double.TryParse(tariffDesciption?.eur_per_hour, NumberStyles.Any, CultureInfo.InvariantCulture, out double euroPerHour) ? euroPerHour : double.NaN,
|
||||
AboEuroPerMonth = double.TryParse(tariffDesciption?.abo_eur_per_month, NumberStyles.Any, CultureInfo.InvariantCulture, out double aboEuroPerMonth) ? aboEuroPerMonth : double.NaN,
|
||||
MaxFeeEuroPerDay = double.TryParse(tariffDesciption?.max_eur_per_day, NumberStyles.Any, CultureInfo.InvariantCulture, out double maxEuroPerDay) ? maxEuroPerDay : double.NaN,
|
||||
OperatorAgb = tariffDesciption?.operator_agb,
|
||||
TrackingInfo = tariffDesciption?.track_info
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary> Creates a booking finished object from response.</summary>
|
||||
/// <param name="response">Response to create survey object from.</param>
|
||||
public static BookingFinishedModel Create(this DoReturnResponse response)
|
||||
{
|
||||
var bookingFinished = new BookingFinishedModel
|
||||
{
|
||||
Co2Saving = response?.co2saving
|
||||
};
|
||||
|
||||
if (response?.user_miniquery == null)
|
||||
|
||||
{
|
||||
return bookingFinished;
|
||||
}
|
||||
|
||||
var miniquery = response.user_miniquery;
|
||||
bookingFinished.MiniSurvey = new MiniSurveyModel
|
||||
{
|
||||
Title = miniquery.title,
|
||||
Subtitle = miniquery.subtitle,
|
||||
Footer = miniquery.footer
|
||||
};
|
||||
|
||||
foreach (var question in miniquery?.questions?.OrderBy(x => x.Key) ?? new Dictionary<string, MiniSurveyResponse.Question>().OrderBy(x => x.Key))
|
||||
{
|
||||
if (string.IsNullOrEmpty(question.Key.Trim())
|
||||
|| question.Value.query == null)
|
||||
{
|
||||
// Skip invalid entries.
|
||||
continue;
|
||||
}
|
||||
|
||||
bookingFinished.MiniSurvey.Questions.Add(
|
||||
question.Key,
|
||||
new MiniSurveyModel.QuestionModel());
|
||||
}
|
||||
|
||||
return bookingFinished;
|
||||
}
|
||||
|
||||
/// <summary> Creates a survey object from response.</summary>
|
||||
/// <param name="response">Response to create survey object from.</param>
|
||||
public static MiniSurveyModel Create(this ReservationCancelReturnResponse response)
|
||||
{
|
||||
if (response?.user_miniquery == null)
|
||||
{
|
||||
return new MiniSurveyModel();
|
||||
}
|
||||
|
||||
var miniquery = response.user_miniquery;
|
||||
var survey = new MiniSurveyModel
|
||||
{
|
||||
Title = miniquery.title,
|
||||
Subtitle = miniquery.subtitle,
|
||||
Footer = miniquery.footer
|
||||
};
|
||||
|
||||
foreach (var question in miniquery?.questions?.OrderBy(x => x.Key) ?? new Dictionary<string, MiniSurveyResponse.Question>().OrderBy(x => x.Key))
|
||||
{
|
||||
if (string.IsNullOrEmpty(question.Key.Trim())
|
||||
|| question.Value.query == null)
|
||||
{
|
||||
// Skip invalid entries.
|
||||
continue;
|
||||
}
|
||||
|
||||
survey.Questions.Add(
|
||||
question.Key,
|
||||
new MiniSurveyModel.QuestionModel());
|
||||
}
|
||||
|
||||
return survey;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,419 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Settings;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Model.Settings;
|
||||
using TINK.Model.Logging;
|
||||
using Serilog.Events;
|
||||
using Serilog.Core;
|
||||
using Serilog;
|
||||
using Plugin.Connectivity;
|
||||
using System.Threading;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Model.Services.Geolocation;
|
||||
using TINK.Model.Services.CopriApi.ServerUris;
|
||||
using TINK.Services.BluetoothLock.Crypto;
|
||||
using TINK.ViewModel.Map;
|
||||
using TINK.ViewModel.Settings;
|
||||
using TINK.Services;
|
||||
using TINK.Services.BluetoothLock.BLE;
|
||||
using Xamarin.Forms;
|
||||
using TINK.Model.Station;
|
||||
|
||||
namespace TINK.Model
|
||||
{
|
||||
[DataContract]
|
||||
public class TinkApp : ITinkApp
|
||||
{
|
||||
/// <summary> Delegate used by login view to commit user name and password. </summary>
|
||||
/// <param name="p_strMailAddress">Mail address used as id login.</param>
|
||||
/// <param name="p_strPassword">Password for login.</param>
|
||||
/// <returns>True if setting credentials succeeded.</returns>
|
||||
public delegate bool SetCredentialsDelegate(string p_strMailAddress, string p_strPassword);
|
||||
|
||||
/// <summary>Returns the id of the app (sharee.bike) to be identified by copri.</summary>
|
||||
public static string MerchantId => "oiF2kahH";
|
||||
|
||||
/// <summary>
|
||||
/// Holds status about whants new page.
|
||||
/// </summary>
|
||||
public WhatsNew WhatsNew { get; private set; }
|
||||
|
||||
/// <summary>Sets flag whats new page was already shown to true. </summary>
|
||||
public void SetWhatsNewWasShown() => WhatsNew = WhatsNew.SetWasShown();
|
||||
|
||||
/// <summary>Holds uris of copri servers. </summary>
|
||||
public CopriServerUriList Uris { get; }
|
||||
|
||||
/// <summary> Holds the filters loaded from settings. </summary>
|
||||
public IGroupFilterSettings FilterGroupSetting { get; set; }
|
||||
|
||||
/// <summary> Holds the filter which is applied on the map view. Either TINK or Konrad stations are displayed. </summary>
|
||||
private IGroupFilterMapPage m_oFilterDictionaryMapPage;
|
||||
|
||||
/// <summary> Holds the filter which is applied on the map view. Either TINK or Konrad stations are displayed. </summary>
|
||||
public IGroupFilterMapPage GroupFilterMapPage
|
||||
{
|
||||
get => m_oFilterDictionaryMapPage;
|
||||
set => m_oFilterDictionaryMapPage = value ?? new GroupFilterMapPage();
|
||||
}
|
||||
|
||||
/// <summary> Value indicating whether map is centerted to current position or not. </summary>
|
||||
public bool CenterMapToCurrentLocation { get; set; }
|
||||
|
||||
/// <summary> Holds the map area to display. </summary>
|
||||
public Xamarin.Forms.GoogleMaps.MapSpan MapSpan { get; set; }
|
||||
|
||||
/// <summary> Gets the minimum logging level. </summary>
|
||||
public LogEventLevel MinimumLogEventLevel { get; set; }
|
||||
|
||||
/// <summary> Gets a value indicating whether reporting level is verbose or not.</summary>
|
||||
public bool IsReportLevelVerbose { get; set; }
|
||||
|
||||
/// <summary> Holds the uri which is applied after restart. </summary>
|
||||
public Uri NextActiveUri { get; set; }
|
||||
|
||||
/// <summary> Saves object to file. </summary>
|
||||
public void Save()
|
||||
=> JsonSettingsDictionary.Serialize(
|
||||
SettingsFileFolder,
|
||||
new Dictionary<string, string>()
|
||||
.SetGroupFilterMapPage(GroupFilterMapPage)
|
||||
.SetCopriHostUri(NextActiveUri.AbsoluteUri)
|
||||
.SetPollingParameters(Polling)
|
||||
.SetGroupFilterSettings(FilterGroupSetting)
|
||||
.SetAppVersion(AppVersion)
|
||||
.SetMinimumLoggingLevel(MinimumLogEventLevel)
|
||||
.SetIsReportLevelVerbose(IsReportLevelVerbose)
|
||||
.SetExpiresAfter(ExpiresAfter)
|
||||
.SetWhatsNew(AppVersion)
|
||||
.SetActiveLockService(LocksServices.Active.GetType().FullName)
|
||||
.SetActiveGeolocationService(GeolocationServices.Active.GetType().FullName)
|
||||
.SetCenterMapToCurrentLocation(CenterMapToCurrentLocation)
|
||||
.SetLogToExternalFolder(LogToExternalFolder)
|
||||
.SetConnectTimeout(LocksServices.Active.TimeOut.MultiConnect)
|
||||
.SetIsSiteCachingOn(IsSiteCachingOn)
|
||||
.SetActiveTheme(Themes.Active.GetType().FullName));
|
||||
|
||||
/// <summary>
|
||||
/// Update connector from filters when
|
||||
/// - login state changes
|
||||
/// - view is toggled (TINK to Kornrad and vice versa)
|
||||
/// </summary>
|
||||
public void UpdateConnector()
|
||||
{
|
||||
// Create filtered connector.
|
||||
m_oConnector = FilteredConnectorFactory.Create(
|
||||
FilterGroupSetting.DoFilter(ActiveUser.DoFilter(GroupFilterMapPage.DoFilter())),
|
||||
m_oConnector.Connector);
|
||||
}
|
||||
|
||||
/// <summary>Polling periode.</summary>
|
||||
public PollingParameters Polling { get; set; }
|
||||
|
||||
public TimeSpan ExpiresAfter { get; set; }
|
||||
|
||||
/// <summary> Holds the version of the app.</summary>
|
||||
public Version AppVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the default polling value.
|
||||
/// </summary>
|
||||
#if USCSHARP9
|
||||
public TimeSpan DefaultPolling => new (0, 0, 10);
|
||||
#else
|
||||
public TimeSpan DefaultPolling => new TimeSpan(0, 0, 10);
|
||||
#endif
|
||||
|
||||
/// <summary> Constructs TinkApp object. </summary>
|
||||
/// <param name="settings"></param>
|
||||
/// <param name="accountStore"></param>
|
||||
/// <param name="passwordValidator"></param>
|
||||
/// <param name="p_oConnectorFactory"></param>
|
||||
/// <param name="geolocationService">Null in productive context. Service to querry geoloation for testing purposes. Parameter can be made optional.</param>
|
||||
/// <param name="locksService">Null in productive context. Service to control locks/ get locks information for testing proposes. Parameter can be made optional.</param>
|
||||
/// <param name="device">Object allowing platform specific operations.</param>
|
||||
/// <param name="specialFolder"></param>
|
||||
/// <param name="p_oDateTimeProvider"></param>
|
||||
/// <param name="isConnectedFunc">True if connector has access to copri server, false if cached values are used.</param>
|
||||
/// <param name="currentVersion">Version of the app. If null version is set to a fixed dummy value (3.0.122) for testing purposes.</param>
|
||||
/// <param name="lastVersion">Version of app which was used before this session.</param>
|
||||
/// <param name="whatsNewShownInVersion"> Holds
|
||||
/// - the version when whats new info was shown last or
|
||||
/// - version of application used last if whats new functionality was not implemented in this version or
|
||||
/// - null if app is installed for the first time.
|
||||
/// /// </param>
|
||||
public TinkApp(
|
||||
Settings.Settings settings,
|
||||
IStore accountStore,
|
||||
Func<bool, Uri, string /* session cookie*/, string /* mail address*/, TimeSpan, IConnector> connectorFactory,
|
||||
IServicesContainer<IGeolocation> geolocationServicesContainer,
|
||||
ILocksService locksService,
|
||||
ISmartDevice device,
|
||||
ISpecialFolder specialFolder,
|
||||
ICipher cipher,
|
||||
object arendiCentral = null,
|
||||
Func<bool> isConnectedFunc = null,
|
||||
Action<SendOrPostCallback, object> postAction = null,
|
||||
Version currentVersion = null,
|
||||
Version lastVersion = null,
|
||||
Version whatsNewShownInVersion = null)
|
||||
{
|
||||
PostAction = postAction
|
||||
?? ((d, obj) => d(obj));
|
||||
|
||||
ConnectorFactory = connectorFactory
|
||||
?? throw new ArgumentException("Can not instantiate TinkApp- object. No connector factory object available.");
|
||||
|
||||
Cipher = cipher ?? new Cipher();
|
||||
|
||||
var locksServices = locksService != null
|
||||
? new HashSet<ILocksService> { locksService }
|
||||
: new HashSet<ILocksService> {
|
||||
new LockItByScanServiceEventBased(Cipher),
|
||||
new LockItByScanServicePolling(Cipher),
|
||||
new LockItByGuidService(Cipher),
|
||||
#if BLUETOOTHLE // Requires LockItBluetoothle library.
|
||||
new Bluetoothle.LockItByGuidService(Cipher),
|
||||
#endif
|
||||
#if ARENDI // Requires LockItArendi library.
|
||||
new Arendi.LockItByGuidService(Cipher, arendiCentral),
|
||||
new Arendi.LockItByScanService(Cipher, arendiCentral),
|
||||
#endif
|
||||
new LocksServiceInReach(),
|
||||
new LocksServiceOutOfReach(),
|
||||
};
|
||||
|
||||
LocksServices = new LocksServicesContainerMutable(
|
||||
lastVersion >= new Version(3, 0, 173) ? settings.ActiveLockService : LocksServicesContainerMutable.DefaultLocksservice,
|
||||
locksServices);
|
||||
|
||||
LocksServices.SetTimeOut(settings.ConnectTimeout);
|
||||
|
||||
Themes = new ServicesContainerMutable<object>(
|
||||
new HashSet<object> {
|
||||
new Themes.Konrad(),
|
||||
new Themes.ShareeBike(),
|
||||
new Themes.LastenradBayern()
|
||||
},
|
||||
settings.ActiveTheme);
|
||||
|
||||
GeolocationServices = geolocationServicesContainer
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(TinkApp)}- object. No geolocation services container object available.");
|
||||
|
||||
FilterGroupSetting = settings.GroupFilterSettings;
|
||||
GroupFilterMapPage = settings.GroupFilterMapPage;
|
||||
|
||||
CenterMapToCurrentLocation = settings.CenterMapToCurrentLocation;
|
||||
|
||||
MapSpan = settings.MapSpan;
|
||||
|
||||
SmartDevice = device
|
||||
?? throw new ArgumentException("Can not instantiate TinkApp- object. No device information provider available.");
|
||||
|
||||
if (specialFolder == null)
|
||||
{
|
||||
throw new ArgumentException("Can not instantiate TinkApp- object. No special folder provider available.");
|
||||
}
|
||||
|
||||
// Set logging level.
|
||||
Level.MinimumLevel = settings.MinimumLogEventLevel;
|
||||
|
||||
LogToExternalFolder = settings.LogToExternalFolder;
|
||||
|
||||
IsSiteCachingOn = settings.IsSiteCachingOn;
|
||||
|
||||
ExternalFolder = specialFolder.GetExternalFilesDir();
|
||||
|
||||
SettingsFileFolder = specialFolder.GetInternalPersonalDir();
|
||||
|
||||
ActiveUser = new User.User(
|
||||
accountStore.GetType().Name == "StoreLegacy" ? new Store() : accountStore,
|
||||
accountStore.Load().Result,
|
||||
device.Identifier);
|
||||
|
||||
this.isConnectedFunc = isConnectedFunc ?? (() => CrossConnectivity.Current.IsConnected);
|
||||
|
||||
ExpiresAfter = settings.ExpiresAfter;
|
||||
|
||||
// Create filtered connector for offline mode.
|
||||
m_oConnector = FilteredConnectorFactory.Create(
|
||||
FilterGroupSetting.DoFilter(GroupFilterMapPage.DoFilter()),
|
||||
ConnectorFactory(GetIsConnected(), settings.ActiveUri, ActiveUser.SessionCookie, ActiveUser.Mail, ExpiresAfter));
|
||||
|
||||
// Get uris from file.
|
||||
// Initialize all settings to defaults
|
||||
// Process uris.
|
||||
Uris = new CopriServerUriList(settings.ActiveUri);
|
||||
|
||||
NextActiveUri = Uris.ActiveUri;
|
||||
|
||||
Polling = settings.PollingParameters ??
|
||||
throw new ArgumentException("Can not instantiate TinkApp- object. Polling parameters must never be null.");
|
||||
|
||||
AppVersion = currentVersion ?? new Version(3, 0, 122);
|
||||
|
||||
MinimumLogEventLevel = settings.MinimumLogEventLevel;
|
||||
|
||||
IsReportLevelVerbose = settings.IsReportLevelVerbose;
|
||||
|
||||
WhatsNew = new WhatsNew(AppVersion, lastVersion, whatsNewShownInVersion);
|
||||
|
||||
if (Themes.Active.GetType().FullName == typeof(Themes.ShareeBike).FullName)
|
||||
{
|
||||
// Nothing to do.
|
||||
// Theme to activate is default theme.
|
||||
return;
|
||||
}
|
||||
|
||||
// Set active app theme
|
||||
ICollection<ResourceDictionary> mergedDictionaries = Application.Current.Resources.MergedDictionaries;
|
||||
if (mergedDictionaries == null)
|
||||
{
|
||||
Log.ForContext<TinkApp>().Error("No merged dictionary available.");
|
||||
return;
|
||||
}
|
||||
|
||||
mergedDictionaries.Clear();
|
||||
|
||||
if (Themes.Active.GetType().FullName == typeof(Themes.Konrad).FullName)
|
||||
{
|
||||
mergedDictionaries.Add(new Themes.Konrad());
|
||||
}
|
||||
else if (Themes.Active.GetType().FullName == typeof(Themes.LastenradBayern).FullName)
|
||||
{
|
||||
mergedDictionaries.Add(new Themes.LastenradBayern());
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<TinkApp>().Debug($"No theme {Themes.Active} found.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Holds the user of the app. </summary>
|
||||
[DataMember]
|
||||
public User.User ActiveUser { get; }
|
||||
|
||||
/// <summary> Reference of object which provides device information. </summary>
|
||||
public ISmartDevice SmartDevice { get; }
|
||||
|
||||
/// <summary> Holds delegate to determine whether device is connected or not.</summary>
|
||||
private readonly Func<bool> isConnectedFunc;
|
||||
|
||||
/// <summary> Gets whether device is connected to internet or not. </summary>
|
||||
public bool GetIsConnected() => isConnectedFunc();
|
||||
|
||||
/// <summary> Holds the folder where settings files are stored. </summary>
|
||||
public string SettingsFileFolder { get; }
|
||||
|
||||
/// <summary> Holds folder parent of the folder where log files are stored. </summary>
|
||||
public string LogFileParentFolder => LogToExternalFolder && !string.IsNullOrEmpty(ExternalFolder) ? ExternalFolder : SettingsFileFolder;
|
||||
|
||||
/// <summary> Holds a value indicating whether to log to external or internal folder. </summary>
|
||||
public bool LogToExternalFolder { get; set; }
|
||||
|
||||
/// <summary> Holds a value indicating whether Site caching is on or off. </summary>
|
||||
public bool IsSiteCachingOn { get; set; }
|
||||
|
||||
/// <summary> External folder. </summary>
|
||||
public string ExternalFolder { get; }
|
||||
|
||||
public ICipher Cipher { get; }
|
||||
|
||||
/// <summary> Name of the station which is selected. </summary>
|
||||
public IStation SelectedStation { get; set; } = new NullStation();
|
||||
|
||||
/// <summary> Holds the stations availalbe. </summary>
|
||||
public IEnumerable<IStation> Stations { get; set; } = new List<Station.Station>();
|
||||
|
||||
/// <summary> Action to post to GUI thread.</summary>
|
||||
public Action<SendOrPostCallback, object> PostAction { get; }
|
||||
|
||||
/// <summary> Function which creates a connector depending on connected status.</summary>
|
||||
private Func<bool, Uri, string /*userAgent*/, string /*sessionCookie*/, TimeSpan, IConnector> ConnectorFactory { get; }
|
||||
|
||||
/// <summary> Holds the object which provides offline data.</summary>
|
||||
private IFilteredConnector m_oConnector;
|
||||
|
||||
/// <summary> Holds the system to copri.</summary>
|
||||
public IFilteredConnector GetConnector(bool isConnected)
|
||||
{
|
||||
if (m_oConnector.IsConnected == isConnected
|
||||
&& m_oConnector.Command.SessionCookie == ActiveUser.SessionCookie)
|
||||
{
|
||||
// Neither connection nor logged in stated changed.
|
||||
return m_oConnector;
|
||||
}
|
||||
|
||||
// Connected state changed. New connection object has to be created.
|
||||
m_oConnector = FilteredConnectorFactory.Create(
|
||||
FilterGroupSetting.DoFilter(ActiveUser.DoFilter(GroupFilterMapPage.DoFilter())),
|
||||
ConnectorFactory(
|
||||
isConnected,
|
||||
Uris.ActiveUri,
|
||||
ActiveUser.SessionCookie,
|
||||
ActiveUser.Mail,
|
||||
ExpiresAfter));
|
||||
|
||||
return m_oConnector;
|
||||
}
|
||||
|
||||
/// <summary> Manages the different types of LocksService objects.</summary>
|
||||
public LocksServicesContainerMutable LocksServices { get; set; }
|
||||
|
||||
/// <summary> Holds available app themes.</summary>
|
||||
public IServicesContainer<IGeolocation> GeolocationServices { get; }
|
||||
|
||||
/// <summary> Manages the different types of LocksService objects.</summary>
|
||||
public ServicesContainerMutable<object> Themes { get; }
|
||||
|
||||
/// <summary> Object to switch logging level. </summary>
|
||||
private LoggingLevelSwitch m_oLoggingLevelSwitch;
|
||||
|
||||
/// <summary>
|
||||
/// Object to allow swithing logging level
|
||||
/// </summary>
|
||||
public LoggingLevelSwitch Level
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_oLoggingLevelSwitch == null)
|
||||
{
|
||||
m_oLoggingLevelSwitch = new LoggingLevelSwitch
|
||||
{
|
||||
|
||||
// Set warning level to error.
|
||||
MinimumLevel = Settings.Settings.DEFAULTLOGGINLEVEL
|
||||
};
|
||||
}
|
||||
|
||||
return m_oLoggingLevelSwitch;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Updates logging level. </summary>
|
||||
/// <param name="p_oNewLevel">New level to set.</param>
|
||||
public void UpdateLoggingLevel(LogEventLevel p_oNewLevel)
|
||||
{
|
||||
if (Level.MinimumLevel == p_oNewLevel)
|
||||
{
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
Log.CloseAndFlush(); // Close before modifying logger configuration. Otherwise a sharing vialation occurs.
|
||||
|
||||
Level.MinimumLevel = p_oNewLevel;
|
||||
|
||||
// Update logging
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.ControlledBy(Level)
|
||||
.WriteTo.Debug()
|
||||
.WriteTo.File(LogFileParentFolder, Logging.RollingInterval.Session)
|
||||
.CreateLogger();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue