Unused files removed and differences updated.

This commit is contained in:
Oliver Hauff 2022-01-23 13:08:18 +01:00
parent 9793a71d6f
commit af3c20ea1c
129 changed files with 22 additions and 44291 deletions

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}