mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-04-22 21:06:30 +02:00
Version 3.0.375
This commit is contained in:
parent
2c790239cb
commit
ca080c87c0
194 changed files with 10092 additions and 10464 deletions
|
@ -104,7 +104,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
|
|||
/// <summary>
|
||||
/// Converts the instance to text.
|
||||
/// </summary>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Id={Bike.Id}{(Bike.WheelType != null ? $", wheel(s)={Bike.WheelType}" : string.Empty)}{(Bike.TypeOfBike != null ? $"type={Bike.TypeOfBike}" : "")}, state={State}, location={(!string.IsNullOrEmpty(StationId) ? $"Station {StationId}" : "On the road")}, is demo={IsDemo}.";
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
|
|||
/// <summary>
|
||||
/// Converts the instance to text.
|
||||
/// </summary>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Id={Id}{(WheelType != null ? $", wheel(s)={WheelType}" : string.Empty)}{(TypeOfBike != null ? $", type={TypeOfBike}" : "")}, demo={IsDemo}, state={State.ToString()}, location={(!string.IsNullOrEmpty(StationId) ? $"Station {StationId}" : "On the road")}.";
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
|
|||
/// </summary>
|
||||
Uri OperatorUri { get; }
|
||||
|
||||
/// <summary> Holds description about the tarif. </summary>
|
||||
/// <summary> Holds description about the tariff. </summary>
|
||||
RentalDescription TariffDescription { get; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -107,7 +107,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BikeNS
|
|||
}
|
||||
|
||||
/// <summary> Converts the instance to text.</summary>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return WheelType == null || TypeOfBike == null
|
||||
? $"Id={Id}{(!string.IsNullOrEmpty(Description) ? $", {Description}" : "")}"
|
||||
|
|
|
@ -110,9 +110,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
|
|||
LockService,
|
||||
listener: listener);
|
||||
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Id={Id}{(TypeOfBike != null ? $";type={TypeOfBike}" : "")};state={State.ToString()}";
|
||||
return $"Id={Id}{(TypeOfBike != null ? $";type={TypeOfBike}" : "")};state={State.ToString()};Lock id={LockInfo.Id}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ using TINK.Services.BluetoothLock;
|
|||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler;
|
||||
|
||||
namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
||||
{
|
||||
|
@ -91,9 +92,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
{
|
||||
listener.ReportStep(step);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking step- action for set {step} ", ex, step);
|
||||
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking step-action for step {step} ", exception, step);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,9 +108,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
{
|
||||
await listener.ReportStateAsync(state, message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking state- action for set {state} ", ex, state);
|
||||
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking state-action for state {state} ", exception, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,21 +119,21 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
{
|
||||
// Step: Wait until getting geolocation has completed.
|
||||
InvokeCurrentStep(Step.WaitStopPollingQueryLocation);
|
||||
Log.ForContext<T>().Debug($"Waiting on steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} to finish...");
|
||||
Log.ForContext<T>().Information($"Waiting on steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} to finish...");
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(new List<Task> { locationTask, stopPollingTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<T>().Information("Canceling query location/ wait for polling task to finish failed. {Exception}", ex);
|
||||
await InvokeCurrentStateAsync(State.WaitGeolocationException, ex.Message);
|
||||
Log.ForContext<T>().Information("Canceling query location/ wait for polling task to finish failed. {@exception}", exception);
|
||||
await InvokeCurrentStateAsync(State.WaitGeolocationException, exception.Message);
|
||||
InvokeCurrentStep(Step.QueryLocationTerminated);
|
||||
return null;
|
||||
}
|
||||
|
||||
Log.ForContext<T>().Debug($"Steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} finished.");
|
||||
Log.ForContext<T>().Information($"Steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} finished.");
|
||||
InvokeCurrentStep(Step.QueryLocationTerminated);
|
||||
return locationTask.Result;
|
||||
}
|
||||
|
@ -155,21 +156,23 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
Age = timeStamp.Subtract(location.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
Log.ForContext<T>().Information("Backend updated for bike {bikeId} successfully.", bike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed.", bike.Id);
|
||||
//BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<T>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", bike);
|
||||
Log.ForContext<T>().Debug("Copri server not reachable.");
|
||||
await InvokeCurrentStateAsync(State.WebConnectFailed, exception.Message);
|
||||
return;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<T>().Information("User locked bike {bike} in order to pause ride but updating failed. Message: {Message} Details: {Details}", bike, copriException.Message, copriException.Response);
|
||||
Log.ForContext<T>().Debug("Message: {Message} Details: {Details}", copriException.Message, copriException.Response);
|
||||
await InvokeCurrentStateAsync(State.ResponseIsInvalid, exception.Message);
|
||||
return;
|
||||
}
|
||||
|
@ -192,48 +195,49 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
try
|
||||
{
|
||||
currentLocationTask = geolocation.GetAsync(ctsLocation.Token, timeStampNow);
|
||||
Log.ForContext<T>().Information("Starting query location successful.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<T>().Information("Starting query location failed. {Exception}", bike, ex);
|
||||
await InvokeCurrentStateAsync(State.StartGeolocationException, ex.Message);
|
||||
Log.ForContext<T>().Information("Starting query location failed. {@exception}", exception);
|
||||
await InvokeCurrentStateAsync(State.StartGeolocationException, exception.Message);
|
||||
}
|
||||
|
||||
//// Step: Close lock.
|
||||
IGeolocation currentLocation;
|
||||
Log.ForContext<T>().Debug($"Starting step {Step.ClosingLock}...");
|
||||
Log.ForContext<T>().Information($"Starting step {Step.ClosingLock}...");
|
||||
InvokeCurrentStep(Step.ClosingLock);
|
||||
LockitLockingState? lockingState;
|
||||
try
|
||||
{
|
||||
lockingState = await lockService[bike.LockInfo.Id].CloseAsync();
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} closed successfully.", bike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} can not be closed.", bike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<T>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
Log.ForContext<T>().Debug("Lock is out of reach");
|
||||
await InvokeCurrentStateAsync(State.OutOfReachError, exception.Message);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<T>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
Log.ForContext<T>().Debug("Lock is moving.");
|
||||
await InvokeCurrentStateAsync(State.CouldntCloseMovingError, exception.Message);
|
||||
}
|
||||
else if (exception is CouldntCloseBoltBlockedException)
|
||||
{
|
||||
Log.ForContext<T>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
Log.ForContext<T>().Debug("Bold is blocked.}");
|
||||
await InvokeCurrentStateAsync(State.CouldntCloseBoltBlockedError, exception.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
Log.ForContext<T>().Debug("{@exception}", exception);
|
||||
await InvokeCurrentStateAsync(State.GeneralCloseError, exception.Message);
|
||||
}
|
||||
|
||||
Log.ForContext<T>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
|
|
|
@ -75,9 +75,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
try
|
||||
{
|
||||
listener.ReportStep(step);
|
||||
} catch (Exception ex)
|
||||
} catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking step- action for set {step} ", ex, step);
|
||||
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking step-action for step {step} ", exception, step);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,9 +91,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
{
|
||||
await listener.ReportStateAsync(state, message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking state- action for set {state} ", ex, state);
|
||||
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking state-action for state {state} ", exception, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,8 +121,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
if (deviceState != DeviceState.Connected)
|
||||
{
|
||||
// Geolocation can not be queried because bike is not around.
|
||||
Log.ForContext<T>().Information("User selected booked bike {bike} but returning failed. There is no geolocation information available.", bike);
|
||||
|
||||
Log.ForContext<T>().Information("User selected booked bike {bikeId} but returning failed. There is no geolocation information available.", bike.Id);
|
||||
await InvokeCurrentStateAsync(State.DisconnetedNoLocationError, "");
|
||||
|
||||
//// Step: Disconnect lock.
|
||||
|
@ -130,11 +129,11 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
try
|
||||
{
|
||||
bike.LockInfo.State = await lockService.DisconnectAsync(bike.LockInfo.Id, bike.LockInfo.Guid);
|
||||
Log.ForContext<T>().Information("Lock from bike {bikeId} disconnected successfully.", bike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
Log.ForContext<T>().Information("Lock from bike {bikeId} can not be disconnected. {@exception}", bike.Id, exception);
|
||||
await InvokeCurrentStateAsync(State.DisconnectError, exception.Message);
|
||||
}
|
||||
|
||||
|
@ -148,20 +147,19 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|||
try
|
||||
{
|
||||
closingLockLocation = await geolocation.GetAsync(ctsLocation.Token, DateTime.Now);
|
||||
Log.ForContext<T>().Information("Query location of lock from bike {bikeId} successful.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<T>().Information("Returning closed bike {Bike} is not possible. Geolocation query failed. {Exception}", bike, ex);
|
||||
|
||||
await InvokeCurrentStateAsync(State.QueryLocationFailed, ex.Message);
|
||||
Log.ForContext<T>().Information("Geolocation query failed. {@exception}", exception);
|
||||
await InvokeCurrentStateAsync(State.QueryLocationFailed, exception.Message);
|
||||
throw;
|
||||
}
|
||||
|
||||
await InvokeCurrentStateAsync(State.QueryLocationSucceeded, string.Empty);
|
||||
|
||||
// Update last lock state time
|
||||
|
||||
// save geolocation data for sending to backend
|
||||
var currentLocationDto = closingLockLocation != null
|
||||
? new LocationDto.Builder
|
||||
|
|
|
@ -185,7 +185,7 @@ namespace TINK.Model.Connector
|
|||
|
||||
}
|
||||
|
||||
/// <summary> Updates COPRI lock state for a booked bike. </summary>
|
||||
/// <summary> Updates COPRI lock state for a booked or reserved bike. </summary>
|
||||
/// <param name="bike">Bike to update locking state for.</param>
|
||||
/// <param name="location">Location of the bike.</param>
|
||||
/// <returns>Response on updating locking state.</returns>
|
||||
|
@ -198,7 +198,7 @@ namespace TINK.Model.Connector
|
|||
throw new ArgumentNullException("Can not update locking state of bike. No bike object available.");
|
||||
}
|
||||
|
||||
if (bike.State.Value != State.InUseStateEnum.Booked)
|
||||
if (bike.State.Value != State.InUseStateEnum.Booked && bike.State.Value != State.InUseStateEnum.Reserved)
|
||||
{
|
||||
throw new ArgumentNullException($"Can not update locking state of bike. Unexpected booking state {bike.State} detected.");
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@ namespace TINK.Model.Connector
|
|||
public class ConnectorCache : IConnector
|
||||
{
|
||||
/// <summary>Constructs a copri connector object to connect to cache.</summary>
|
||||
/// <remarks>Used for offline szenario to ensure responsiveness of app by preventing hopeless tries to communicate with COPRI. </remarks>
|
||||
/// <remarks>Used for offline scenario to ensure responsiveness of app by preventing hopeless tries to communicate with COPRI. </remarks>
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
/// <param name="sessionCookie"> Holds the session cookie.</param>
|
||||
/// <param name="mail">Mail of user.</param>
|
||||
/// <param name="smartDevice">Holds info about smart device.</param>
|
||||
/// <param name="server"> Is null in production and migh be a mock in testing context.</param>
|
||||
/// <param name="server"> Is null in production and might be a mock in testing context.</param>
|
||||
public ConnectorCache(
|
||||
AppContextInfo appContextInfo,
|
||||
string uiIsoLangugageName,
|
||||
|
@ -36,10 +36,10 @@ namespace TINK.Model.Connector
|
|||
mail);
|
||||
}
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
/// <summary> Object for querying stations and bikes.</summary>
|
||||
public ICommand Command { get; private set; }
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
/// <summary> Object for querying stations and bikes.</summary>
|
||||
public IQuery Query { get; private set; }
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
|
|
|
@ -64,9 +64,10 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesAsync();
|
||||
var result = await m_oInnerQuery.GetBikesAsync(operatorUri);
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
new BikeCollection(DoFilter(result.Response, Filter)),
|
||||
|
@ -129,6 +130,7 @@ namespace TINK.Model.Connector
|
|||
station.Group,
|
||||
station.Position,
|
||||
station.StationName,
|
||||
station.OperatorUri,
|
||||
station.OperatorData,
|
||||
new BikeGroupCol(station.BikeGroups
|
||||
.Where(group => filter.DoFilter(new List<string> { group.Group }).Count() > 0))) as IStation)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
namespace TINK.Model.Connector
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public interface IConnector
|
||||
{
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
/// <summary> Object for querying stations and bikes.</summary>
|
||||
ICommand Command { get; }
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
/// <summary> Object for querying stations and bikes.</summary>
|
||||
IQuery Query { get; }
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
|
|
|
@ -56,9 +56,10 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesAsync();
|
||||
var result = await m_oInnerQuery.GetBikesAsync(operatorUri);
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
new BikeCollection(result.Response.ToDictionary(x => x.Id)),
|
||||
|
|
|
@ -71,14 +71,15 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
/// <summary> Gets bikes available. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
|
||||
{
|
||||
var result = await server.GetBikesAvailable();
|
||||
var result = await server.GetBikesAvailable(operatorUri: operatorUri);
|
||||
|
||||
if (result.Source != typeof(CopriCallsMonkeyStore))
|
||||
{
|
||||
server.AddToCache(result);
|
||||
server.AddToCache(result, operatorUri);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -127,21 +127,24 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
/// <summary> Gets bikes available and bikes occupied. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
|
||||
{
|
||||
var bikesAvailableResponse = await Server.GetBikesAvailable();
|
||||
var bikesAvailableResponse = await Server.GetBikesAvailable(operatorUri: operatorUri);
|
||||
|
||||
if (bikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesAvailableResponse.Exception != null)
|
||||
{
|
||||
// Bikes available were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies.
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available read from cache. Reading bikes occupied from cache as well.");
|
||||
// Bikes were read from cache.
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and bikes occupied from cache invoking one single call.");
|
||||
return new Result<BikeCollection>(
|
||||
bikesAvailableResponse.Source,
|
||||
BikeCollectionFactory.GetBikesAll(
|
||||
bikesAvailableResponse.Response?.bikes?.Values,
|
||||
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
|
||||
operatorUri?.AbsoluteUri == null ?
|
||||
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values // Get bikes occupied from cache as well to avoid inconsistencies.
|
||||
: bikesAvailableResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider,
|
||||
Bikes.BikeInfoNS.BC.DataSource.Cache),
|
||||
|
@ -149,7 +152,27 @@ namespace TINK.Model.Connector
|
|||
bikesAvailableResponse.Exception);
|
||||
}
|
||||
|
||||
var bikesOccupiedResponse = await Server.GetBikesOccupied();
|
||||
if (operatorUri?.AbsoluteUri != null)
|
||||
{
|
||||
// Both types bikes could read from copri successfully => update cache
|
||||
Server.AddToCache(bikesAvailableResponse, operatorUri);
|
||||
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and occupied read successfully from server invoking one single request.");
|
||||
return new Result<BikeCollection>(
|
||||
bikesAvailableResponse.Source,
|
||||
BikeCollectionFactory.GetBikesAll(
|
||||
bikesAvailableResponse.Response?.bikes?.Values,
|
||||
bikesAvailableResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider,
|
||||
Bikes.BikeInfoNS.BC.DataSource.Copri),
|
||||
bikesAvailableResponse.GeneralData,
|
||||
bikesAvailableResponse.Exception != null ? new AggregateException(new[] { bikesAvailableResponse.Exception }) : null);
|
||||
}
|
||||
|
||||
/// Legacy implementation: GetBikesOccupied are not returned in <see cref="ICachedCopriServer.GetBikesAvailable"/> call.
|
||||
/// A separate call <see cref="ICachedCopriServer.GetBikesOccupied"/> is required to retrieve all bikes.
|
||||
var bikesOccupiedResponse = await Server.GetBikesOccupied(); /* Only query bikes occupied if operator uri is unknown. */
|
||||
if (bikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesOccupiedResponse.Exception != null)
|
||||
{
|
||||
|
@ -158,7 +181,7 @@ namespace TINK.Model.Connector
|
|||
return new Result<BikeCollection>(
|
||||
bikesOccupiedResponse.Source,
|
||||
BikeCollectionFactory.GetBikesAll(
|
||||
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
|
||||
(await Server.GetBikesAvailable(true, operatorUri)).Response?.bikes?.Values,
|
||||
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider,
|
||||
|
@ -168,7 +191,7 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
// Both types bikes could read from copri => update cache
|
||||
Server.AddToCache(bikesAvailableResponse);
|
||||
Server.AddToCache(bikesAvailableResponse, operatorUri);
|
||||
Server.AddToCache(bikesOccupiedResponse);
|
||||
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and occupied read successfully from server.");
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
|
@ -14,7 +15,8 @@ namespace TINK.Model.Connector
|
|||
Task<Result<BikeCollection>> GetBikesOccupiedAsync();
|
||||
|
||||
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
Task<Result<BikeCollection>> GetBikesAsync();
|
||||
Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,10 +54,11 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
/// <summary> Gets bikes occupied. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns> Collection of bikes. </returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
|
||||
{
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync(operatorUri);
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
bikesAvailableResponse.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache),
|
||||
|
|
|
@ -69,10 +69,11 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
/// <summary> Gets bikes available and bikes occupied. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
|
||||
{
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync(operatorUri);
|
||||
var bikesOccupiedResponse = await server.GetBikesOccupiedAsync();
|
||||
|
||||
return new Result<BikeCollection>(
|
||||
|
|
|
@ -361,9 +361,35 @@ namespace TINK.Model.Connector
|
|||
/// <returns>Operator Uri</returns>
|
||||
public static Uri GetOperatorUri(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
return bikeInfo?.uri_operator != null && !string.IsNullOrEmpty(bikeInfo?.uri_operator)
|
||||
? new Uri($"{bikeInfo.uri_operator}/{CopriServerUriList.REST_RESOURCE_ROOT}")
|
||||
: null;
|
||||
if (Uri.TryCreate(bikeInfo?.uri_operator, UriKind.Absolute, out var operatorUri))
|
||||
{
|
||||
// Valid uri detected.
|
||||
return new Uri($"{operatorUri.AbsoluteUri}/{CopriServerUriList.REST_RESOURCE_ROOT}");
|
||||
}
|
||||
|
||||
Log.Error(!string.IsNullOrEmpty(bikeInfo?.uri_operator)
|
||||
? $"Operator uri can not be extracted from bike info base object {bikeInfo.uri_operator}. Uri is not valid."
|
||||
: "Operator uri can not be extracted from bike info base object. Entry is null or empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operator Uri from response.
|
||||
/// </summary>
|
||||
/// <param name="stationInfo"> Response to get uri from.</param>
|
||||
/// <returns>Operator Uri</returns>
|
||||
public static Uri GetOperatorUri(this StationInfo stationInfo)
|
||||
{
|
||||
if (Uri.TryCreate(stationInfo?.uri_operator, UriKind.Absolute, out var operatorUri))
|
||||
{
|
||||
// Valid uri detected.
|
||||
return new Uri($"{operatorUri.AbsoluteUri}/{CopriServerUriList.REST_RESOURCE_ROOT}");
|
||||
}
|
||||
|
||||
Log.Error(!string.IsNullOrEmpty(stationInfo?.uri_operator)
|
||||
? $"Operator uri can not be extracted from station object {stationInfo.uri_operator}. Uri is not valid."
|
||||
: "Operator uri can not be extracted from station object. Entry is null or empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary> Tries to get the copri version from response.</summary>
|
||||
|
@ -415,6 +441,7 @@ namespace TINK.Model.Connector
|
|||
station.GetGroup(),
|
||||
station.GetPosition(),
|
||||
station.description,
|
||||
station.GetOperatorUri(),
|
||||
new Data(station.operator_data?.operator_name,
|
||||
station.operator_data?.operator_phone,
|
||||
station.operator_data?.operator_hours,
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace TINK.Model.Connector.Updater
|
|||
DataSource dataSource)
|
||||
=> GetBikesAll(
|
||||
bikesAvailableResponse?.bikes?.Values,
|
||||
new BikesReservedOccupiedResponse()?.bikes_occupied?.Values, // There are no occupied bikes.
|
||||
bikesAvailableResponse?.bikes_occupied?.Values,
|
||||
string.Empty,
|
||||
() => DateTime.Now,
|
||||
dataSource);
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace TINK.Model.State
|
|||
/// Transforms object to string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return _InUseState.Value.ToString("g");
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ namespace TINK.Model.State
|
|||
/// Transforms object to string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return _StateInfo.ToString();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.Stations.StationNS.Operator;
|
||||
|
||||
|
@ -17,6 +18,9 @@ namespace TINK.Model.Stations.StationNS
|
|||
/// <summary> Gets the name of the station.</summary>
|
||||
string StationName { get; }
|
||||
|
||||
/// <summary> Uri of the operator or null. </summary>
|
||||
Uri OperatorUri { get; }
|
||||
|
||||
/// <summary> Holds the gps- position of the station.</summary>
|
||||
IPosition Position { get; }
|
||||
|
||||
|
@ -29,5 +33,6 @@ namespace TINK.Model.Stations.StationNS
|
|||
/// <summary> Gets bike <see cref="BikeGroupCol.Entry"/> objects. </summary>
|
||||
/// <remarks> Each entry has a name, holds the count of available bikes at station and the group value. /// </remarks>
|
||||
IBikeGroupCol BikeGroups { get; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.Stations.StationNS.Operator;
|
||||
|
||||
|
@ -15,6 +16,9 @@ namespace TINK.Model.Stations.StationNS
|
|||
/// <summary> Gets the name of the station.</summary>
|
||||
public string StationName => string.Empty;
|
||||
|
||||
/// <summary> Uri of the operator or null. </summary>
|
||||
public Uri OperatorUri => null;
|
||||
|
||||
/// <summary> Holds the gps- position of the station.</summary>
|
||||
public IPosition Position => PositionFactory.Create();
|
||||
|
||||
|
|
|
@ -12,11 +12,13 @@ namespace TINK.Model.Stations.StationNS
|
|||
/// <param name="group">Group (TINK, Konrad) to which station is related.</param>
|
||||
/// <param name="position">GPS- position of the station.</param>
|
||||
/// <param name="stationName">Name of the station.</param>
|
||||
/// <param name="operatorUri">Uri of the operator or null.</param>
|
||||
public Station(
|
||||
string id,
|
||||
IEnumerable<string> group = null,
|
||||
IPosition position = null,
|
||||
string stationName = "",
|
||||
Uri operatorUri = null,
|
||||
IData operatorData = null,
|
||||
IBikeGroupCol bikeGropCol = null)
|
||||
{
|
||||
|
@ -26,6 +28,7 @@ namespace TINK.Model.Stations.StationNS
|
|||
StationName = stationName ?? string.Empty;
|
||||
OperatorData = operatorData ?? new Data();
|
||||
BikeGroups = bikeGropCol ?? new BikeGroupCol();
|
||||
OperatorUri = operatorUri;
|
||||
}
|
||||
|
||||
/// <summary> Holds the unique id of the station.c</summary>
|
||||
|
@ -49,5 +52,8 @@ namespace TINK.Model.Stations.StationNS
|
|||
/// <summary> Gets bike <see cref="BikeGroupCol.Entry"/> objects. </summary>
|
||||
/// <remarks> Each entry has a name, holds the count of available bikes at station and the group value. /// </remarks>
|
||||
public IBikeGroupCol BikeGroups { get; }
|
||||
|
||||
/// <summary> Uri of the operator or null. </summary>
|
||||
public Uri OperatorUri { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,7 +182,6 @@ namespace TINK.Model
|
|||
ISpecialFolder specialFolder,
|
||||
ICipher cipher,
|
||||
ITheme theme,
|
||||
object arendiCentral = null,
|
||||
Action<SendOrPostCallback, object> postAction = null,
|
||||
Version currentVersion = null,
|
||||
Version lastVersion = null,
|
||||
|
@ -225,11 +224,7 @@ namespace TINK.Model
|
|||
#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 LocksServiceInReach(),
|
||||
new LocksServiceOutOfReach(),
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace TINK.Model.User.Account
|
|||
SwitchNoSiteCaching = 1024, // Allows to turn off/ on caching of sites displayed in app hosted by COPRI
|
||||
ReportLevel = 2048, // Allows extent to show error messages.
|
||||
SwitchTheme = 4096, // Allows user to switch theme (sharee.bike, Mein konrad, LastenRad Bayern)
|
||||
ManageAlarmAndSounds = 8192, // Allows user to manage sounds and alarm settings (lock open and connected)
|
||||
All = PickCopriServer +
|
||||
ManageCopriCacheExpiration +
|
||||
ManagePolling +
|
||||
|
@ -28,7 +29,8 @@ namespace TINK.Model.User.Account
|
|||
ShowDiagnostics +
|
||||
SwitchNoSiteCaching +
|
||||
ReportLevel +
|
||||
SwitchTheme,
|
||||
SwitchTheme +
|
||||
ManageAlarmAndSounds,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -732,6 +732,11 @@ namespace TINK.Model
|
|||
AppResources.ChangeLog_3_0_374_MK_SB,
|
||||
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 375),
|
||||
string.Format("{0} <br /> {1} <br /> {2}", AppResources.ChangeLog_MinorImprovements, AppResources.ChangeLog_PackageUpdates, AppResources.ChangeLog_MinorBugFixes),
|
||||
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary> Manges the whats new information.</summary>
|
||||
|
|
|
@ -63,45 +63,45 @@ namespace TINK.MultilingualResources {
|
|||
/// <summary>
|
||||
/// Looks up a localized string similar to Rent bike.
|
||||
/// </summary>
|
||||
public static string ActionBook {
|
||||
public static string ActionRentBike {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionBook", resourceCulture);
|
||||
return ResourceManager.GetString("ActionRentBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel reservation.
|
||||
/// </summary>
|
||||
public static string ActionCancelRequest {
|
||||
public static string ActionCancelReservation {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionCancelRequest", resourceCulture);
|
||||
return ResourceManager.GetString("ActionCancelReservation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Close lock.
|
||||
/// </summary>
|
||||
public static string ActionClose {
|
||||
public static string ActionCloseLock {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionClose", resourceCulture);
|
||||
return ResourceManager.GetString("ActionCloseLock", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Close lock & end rental.
|
||||
/// </summary>
|
||||
public static string ActionCloseAndReturn {
|
||||
public static string ActionCloseLockAndReturn {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionCloseAndReturn", resourceCulture);
|
||||
return ResourceManager.GetString("ActionCloseLockAndReturn", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Close lock or rent bike.
|
||||
/// </summary>
|
||||
public static string ActionCloseOrBook {
|
||||
public static string ActionCloseLockOrBook {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionCloseOrBook", resourceCulture);
|
||||
return ResourceManager.GetString("ActionCloseLockOrBook", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,45 +144,45 @@ namespace TINK.MultilingualResources {
|
|||
/// <summary>
|
||||
/// Looks up a localized string similar to Open lock.
|
||||
/// </summary>
|
||||
public static string ActionOpen {
|
||||
public static string ActionOpenLock {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionOpen", resourceCulture);
|
||||
return ResourceManager.GetString("ActionOpenLock", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open lock & rent bike.
|
||||
/// </summary>
|
||||
public static string ActionOpenAndBook {
|
||||
public static string ActionOpenLockAndRentBike {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionOpenAndBook", resourceCulture);
|
||||
return ResourceManager.GetString("ActionOpenLockAndRentBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open lock.
|
||||
/// </summary>
|
||||
public static string ActionOpenAndPause {
|
||||
public static string ActionOpenLockAndPause {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionOpenAndPause", resourceCulture);
|
||||
return ResourceManager.GetString("ActionOpenLockAndPause", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reserve bike.
|
||||
/// </summary>
|
||||
public static string ActionRequest {
|
||||
public static string ActionReserveBike {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionRequest", resourceCulture);
|
||||
return ResourceManager.GetString("ActionReserveBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to End rental.
|
||||
/// </summary>
|
||||
public static string ActionReturn {
|
||||
public static string ActionEndRental {
|
||||
get {
|
||||
return ResourceManager.GetString("ActionReturn", resourceCulture);
|
||||
return ResourceManager.GetString("ActionEndRental", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3393,6 +3393,15 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Rent bike {0}?.
|
||||
/// </summary>
|
||||
public static string QuestionBookBike {
|
||||
get {
|
||||
return ResourceManager.GetString("QuestionBookBike", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel reservation for bike {0}?.
|
||||
/// </summary>
|
||||
|
|
|
@ -12,31 +12,31 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ActionCloseOrBook" xml:space="preserve">
|
||||
<data name="ActionCloseLockOrBook" xml:space="preserve">
|
||||
<value>Schloss schließen oder Rad mieten</value>
|
||||
</data>
|
||||
<data name="ActionCancelRequest" xml:space="preserve">
|
||||
<data name="ActionCancelReservation" xml:space="preserve">
|
||||
<value>Reservierung aufheben</value>
|
||||
</data>
|
||||
<data name="ActionClose" xml:space="preserve">
|
||||
<data name="ActionCloseLock" xml:space="preserve">
|
||||
<value>Schloss schließen</value>
|
||||
</data>
|
||||
<data name="ActionCloseAndReturn" xml:space="preserve">
|
||||
<data name="ActionCloseLockAndReturn" xml:space="preserve">
|
||||
<value>Schloss schließen & Miete beenden</value>
|
||||
</data>
|
||||
<data name="ActionOpen" xml:space="preserve">
|
||||
<data name="ActionOpenLock" xml:space="preserve">
|
||||
<value>Schloss öffnen</value>
|
||||
</data>
|
||||
<data name="ActionOpenAndBook" xml:space="preserve">
|
||||
<data name="ActionOpenLockAndRentBike" xml:space="preserve">
|
||||
<value>Schloss öffnen & Rad mieten</value>
|
||||
</data>
|
||||
<data name="ActionOpenAndPause" xml:space="preserve">
|
||||
<data name="ActionOpenLockAndPause" xml:space="preserve">
|
||||
<value>Schloss öffnen</value>
|
||||
</data>
|
||||
<data name="ActionRequest" xml:space="preserve">
|
||||
<data name="ActionReserveBike" xml:space="preserve">
|
||||
<value>Rad reservieren</value>
|
||||
</data>
|
||||
<data name="ActionReturn" xml:space="preserve">
|
||||
<data name="ActionEndRental" xml:space="preserve">
|
||||
<value>Miete beenden</value>
|
||||
</data>
|
||||
<data name="MarkingMapPage" xml:space="preserve">
|
||||
|
@ -482,7 +482,7 @@ Bitte kontaktieren sie den Kundensupport!</value>
|
|||
<value>Cache genutzt.</value>
|
||||
</data>
|
||||
<data name="ErrorSubmitFeedbackTitle" xml:space="preserve">
|
||||
<value>Übermittlung der Rückmeldung fehlgeschlagen!</value>
|
||||
<value>Übermittlung des Feedbacks fehlgeschlagen!</value>
|
||||
</data>
|
||||
<data name="ActivityTextUpdating" xml:space="preserve">
|
||||
<value>Aktualisiere....</value>
|
||||
|
@ -772,7 +772,7 @@ Kleinere Fehlerbehebungen.
|
|||
</value>
|
||||
</data>
|
||||
<data name="StatusTextFeedbackPending" xml:space="preserve">
|
||||
<value>Rückmeldung offen.</value>
|
||||
<value>Feedback offen.</value>
|
||||
</data>
|
||||
<data name="ErrorBikesAvailableResponseNotOk" xml:space="preserve">
|
||||
<value>Abfrage der verfügbaren Räder fehlgeschlagen.</value>
|
||||
|
@ -781,7 +781,7 @@ Kleinere Fehlerbehebungen.
|
|||
<value>Neues Schloss unterstützt.</value>
|
||||
</data>
|
||||
<data name="ActionGiveFeedback" xml:space="preserve">
|
||||
<value>Rückmeldung geben</value>
|
||||
<value>Feedback geben</value>
|
||||
</data>
|
||||
<data name="ErrorNoConnectionTitle" xml:space="preserve">
|
||||
<value>Verbindung zum Buchungssystem nicht möglich.</value>
|
||||
|
@ -793,7 +793,7 @@ Kleinere Fehlerbehebungen.
|
|||
<value>E-Mailadresse oder Passwort falsch. Korrigieren Sie Ihre Eingabe und versuchen Sie es erneut.</value>
|
||||
</data>
|
||||
<data name="ActivityTextSubmittingFeedback" xml:space="preserve">
|
||||
<value>Sende Rückmeldung...</value>
|
||||
<value>Sende Feedback...</value>
|
||||
</data>
|
||||
<data name="MarkingFlyoutHeader" xml:space="preserve">
|
||||
<value>Menü</value>
|
||||
|
@ -1208,7 +1208,7 @@ Alternativ:
|
|||
<data name="ErrorSubmitFeedback" xml:space="preserve">
|
||||
<value>Dieser Schritt ist nicht zwingend erforderlich. Möchten Sie dennoch Rückmeldung zum Fahrrad geben, kontaktieren Sie den Kundensupport.</value>
|
||||
</data>
|
||||
<data name="ActionBook" xml:space="preserve">
|
||||
<data name="ActionRentBike" xml:space="preserve">
|
||||
<value>Rad mieten</value>
|
||||
</data>
|
||||
<data name="QuestionLogIn" xml:space="preserve">
|
||||
|
@ -1303,10 +1303,10 @@ Die Kosten werden in den nächsten Tagen automatisch abgebucht. Sorgen Sie für
|
|||
<value>an Station</value>
|
||||
</data>
|
||||
<data name="MarkingRentalProcessEndRentalSecondStep" xml:space="preserve">
|
||||
<value>Rückmeldung geben</value>
|
||||
<value>Feedback geben</value>
|
||||
</data>
|
||||
<data name="MarkingRentalProcessEndRentalSecondStepFinished" xml:space="preserve">
|
||||
<value>Rückmeldung</value>
|
||||
<value>Feedback</value>
|
||||
</data>
|
||||
<data name="MarkingRentalProcessEndRentalThirdStep" xml:space="preserve">
|
||||
<value>Miete beenden</value>
|
||||
|
@ -1326,4 +1326,7 @@ Die Kosten werden in den nächsten Tagen automatisch abgebucht. Sorgen Sie für
|
|||
<data name="ChangeLog_3_0_374_MK_SB" xml:space="preserve">
|
||||
<value>In Ihrer Mietende-Bestätigung steht nun welcher Betrag an Mietkosten automatisch von Ihrem hinterlegten Zahlungsmittel abgebucht werden. Weiterhin können Sie Ihre vergangenen Mieten und dafür anfallenden Kosten jederzeit unter "Konto" einsehen.</value>
|
||||
</data>
|
||||
<data name="QuestionBookBike" xml:space="preserve">
|
||||
<value>Rad {0} mieten?</value>
|
||||
</data>
|
||||
</root>
|
|
@ -117,31 +117,31 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ActionCloseOrBook" xml:space="preserve">
|
||||
<data name="ActionCloseLockOrBook" xml:space="preserve">
|
||||
<value>Close lock or rent bike</value>
|
||||
</data>
|
||||
<data name="ActionCancelRequest" xml:space="preserve">
|
||||
<data name="ActionCancelReservation" xml:space="preserve">
|
||||
<value>Cancel reservation</value>
|
||||
</data>
|
||||
<data name="ActionClose" xml:space="preserve">
|
||||
<data name="ActionCloseLock" xml:space="preserve">
|
||||
<value>Close lock</value>
|
||||
</data>
|
||||
<data name="ActionCloseAndReturn" xml:space="preserve">
|
||||
<data name="ActionCloseLockAndReturn" xml:space="preserve">
|
||||
<value>Close lock & end rental</value>
|
||||
</data>
|
||||
<data name="ActionOpen" xml:space="preserve">
|
||||
<data name="ActionOpenLock" xml:space="preserve">
|
||||
<value>Open lock</value>
|
||||
</data>
|
||||
<data name="ActionOpenAndBook" xml:space="preserve">
|
||||
<data name="ActionOpenLockAndRentBike" xml:space="preserve">
|
||||
<value>Open lock & rent bike</value>
|
||||
</data>
|
||||
<data name="ActionOpenAndPause" xml:space="preserve">
|
||||
<data name="ActionOpenLockAndPause" xml:space="preserve">
|
||||
<value>Open lock</value>
|
||||
</data>
|
||||
<data name="ActionRequest" xml:space="preserve">
|
||||
<data name="ActionReserveBike" xml:space="preserve">
|
||||
<value>Reserve bike</value>
|
||||
</data>
|
||||
<data name="ActionReturn" xml:space="preserve">
|
||||
<data name="ActionEndRental" xml:space="preserve">
|
||||
<value>End rental</value>
|
||||
</data>
|
||||
<data name="ActivityTextMapLoadingStationsAndBikes" xml:space="preserve">
|
||||
|
@ -1300,7 +1300,7 @@ A rental end in the suburbs Litzelstetten, Dingelsdorf, Wallhausen and Dettingen
|
|||
<data name="ErrorSubmitFeedback" xml:space="preserve">
|
||||
<value>This step is not mandatory. If you still want to provide feedback on the bike, contact the operator.</value>
|
||||
</data>
|
||||
<data name="ActionBook" xml:space="preserve">
|
||||
<data name="ActionRentBike" xml:space="preserve">
|
||||
<value>Rent bike</value>
|
||||
</data>
|
||||
<data name="ErrorOpenLockBikeAlreadyBooked" xml:space="preserve">
|
||||
|
@ -1418,4 +1418,7 @@ The costs will be debited automatically within the next days. Ensure sufficient
|
|||
<data name="ChangeLog_3_0_374_MK_SB" xml:space="preserve">
|
||||
<value>In your end-of-rental confirmation, you will now see the amount of rental costs that will be automatically debited from your deposited means of payment. Furthermore, you can view your past rentals and the costs incurred for them at any time under "Account".</value>
|
||||
</data>
|
||||
<data name="QuestionBookBike" xml:space="preserve">
|
||||
<value>Rent bike {0}?</value>
|
||||
</data>
|
||||
</root>
|
|
@ -6,39 +6,39 @@
|
|||
</header>
|
||||
<body>
|
||||
<group id="TINKLIB/MULTILINGUALRESOURCES/APPRESOURCES.RESX" datatype="resx">
|
||||
<trans-unit id="ActionCloseOrBook" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionCloseLockOrBook" translate="yes" xml:space="preserve">
|
||||
<source>Close lock or rent bike</source>
|
||||
<target state="translated">Schloss schließen oder Rad mieten</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionCancelRequest" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionCancelReservation" translate="yes" xml:space="preserve">
|
||||
<source>Cancel reservation</source>
|
||||
<target state="translated">Reservierung aufheben</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionClose" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionCloseLock" translate="yes" xml:space="preserve">
|
||||
<source>Close lock</source>
|
||||
<target state="final">Schloss schließen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionCloseAndReturn" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionCloseLockAndReturn" translate="yes" xml:space="preserve">
|
||||
<source>Close lock & end rental</source>
|
||||
<target state="translated">Schloss schließen & Miete beenden</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionOpen" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionOpenLock" translate="yes" xml:space="preserve">
|
||||
<source>Open lock</source>
|
||||
<target state="final">Schloss öffnen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionOpenAndBook" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionOpenLockAndRentBike" translate="yes" xml:space="preserve">
|
||||
<source>Open lock & rent bike</source>
|
||||
<target state="final">Schloss öffnen & Rad mieten</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionOpenAndPause" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionOpenLockAndPause" translate="yes" xml:space="preserve">
|
||||
<source>Open lock</source>
|
||||
<target state="final">Schloss öffnen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionRequest" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionReserveBike" translate="yes" xml:space="preserve">
|
||||
<source>Reserve bike</source>
|
||||
<target state="final">Rad reservieren</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionReturn" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionEndRental" translate="yes" xml:space="preserve">
|
||||
<source>End rental</source>
|
||||
<target state="translated">Miete beenden</target>
|
||||
</trans-unit>
|
||||
|
@ -646,7 +646,7 @@ Bitte kontaktieren sie den Kundensupport!</target>
|
|||
</trans-unit>
|
||||
<trans-unit id="ErrorSubmitFeedbackTitle" translate="yes" xml:space="preserve">
|
||||
<source>Submitting feedback failed!</source>
|
||||
<target state="translated">Übermittlung der Rückmeldung fehlgeschlagen!</target>
|
||||
<target state="translated">Übermittlung des Feedbacks fehlgeschlagen!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActivityTextUpdating" translate="yes" xml:space="preserve">
|
||||
<source>Updating....</source>
|
||||
|
@ -1049,7 +1049,7 @@ Kleinere Fehlerbehebungen.
|
|||
</trans-unit>
|
||||
<trans-unit id="StatusTextFeedbackPending" translate="yes" xml:space="preserve">
|
||||
<source>Feedback pending.</source>
|
||||
<target state="translated">Rückmeldung offen.</target>
|
||||
<target state="translated">Feedback offen.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ErrorBikesAvailableResponseNotOk" translate="yes" xml:space="preserve">
|
||||
<source>Failed to query available bikes.</source>
|
||||
|
@ -1061,7 +1061,7 @@ Kleinere Fehlerbehebungen.
|
|||
</trans-unit>
|
||||
<trans-unit id="ActionGiveFeedback" translate="yes" xml:space="preserve">
|
||||
<source>Give feedback</source>
|
||||
<target state="translated">Rückmeldung geben</target>
|
||||
<target state="translated">Feedback geben</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ErrorNoConnectionTitle" translate="yes" xml:space="preserve">
|
||||
<source>Connection to booking system not possible.</source>
|
||||
|
@ -1077,7 +1077,7 @@ Kleinere Fehlerbehebungen.
|
|||
</trans-unit>
|
||||
<trans-unit id="ActivityTextSubmittingFeedback" translate="yes" xml:space="preserve">
|
||||
<source>Submitting feedback...</source>
|
||||
<target state="translated">Sende Rückmeldung...</target>
|
||||
<target state="translated">Sende Feedback...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MarkingFlyoutHeader" translate="yes" xml:space="preserve">
|
||||
<source>Menu</source>
|
||||
|
@ -1669,7 +1669,7 @@ Alternativ:
|
|||
<source>This step is not mandatory. If you still want to provide feedback on the bike, contact the operator.</source>
|
||||
<target state="translated">Dieser Schritt ist nicht zwingend erforderlich. Möchten Sie dennoch Rückmeldung zum Fahrrad geben, kontaktieren Sie den Kundensupport.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionBook" translate="yes" xml:space="preserve">
|
||||
<trans-unit id="ActionRentBike" translate="yes" xml:space="preserve">
|
||||
<source>Rent bike</source>
|
||||
<target state="translated">Rad mieten</target>
|
||||
</trans-unit>
|
||||
|
@ -1803,11 +1803,11 @@ Die Kosten werden in den nächsten Tagen automatisch abgebucht. Sorgen Sie für
|
|||
</trans-unit>
|
||||
<trans-unit id="MarkingRentalProcessEndRentalSecondStep" translate="yes" xml:space="preserve">
|
||||
<source>Give feedback</source>
|
||||
<target state="translated">Rückmeldung geben</target>
|
||||
<target state="translated">Feedback geben</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MarkingRentalProcessEndRentalSecondStepFinished" translate="yes" xml:space="preserve">
|
||||
<source>Feedback</source>
|
||||
<target state="translated">Rückmeldung</target>
|
||||
<target state="translated">Feedback</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MarkingRentalProcessEndRentalThirdStep" translate="yes" xml:space="preserve">
|
||||
<source>End rental</source>
|
||||
|
@ -1833,6 +1833,10 @@ Die Kosten werden in den nächsten Tagen automatisch abgebucht. Sorgen Sie für
|
|||
<source>In your end-of-rental confirmation, you will now see the amount of rental costs that will be automatically debited from your deposited means of payment. Furthermore, you can view your past rentals and the costs incurred for them at any time under "Account".</source>
|
||||
<target state="translated">In Ihrer Mietende-Bestätigung steht nun welcher Betrag an Mietkosten automatisch von Ihrem hinterlegten Zahlungsmittel abgebucht werden. Weiterhin können Sie Ihre vergangenen Mieten und dafür anfallenden Kosten jederzeit unter "Konto" einsehen.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="QuestionBookBike" translate="yes" xml:space="preserve">
|
||||
<source>Rent bike {0}?</source>
|
||||
<target state="translated">Rad {0} mieten?</target>
|
||||
</trans-unit>
|
||||
</group>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace TINK.Repository
|
|||
public class CopriCallsHttps : ICopriServer
|
||||
{
|
||||
/// <summary> Builds requests.</summary>
|
||||
private IRequestBuilder requestBuilder;
|
||||
private readonly IRequestBuilder requestBuilder;
|
||||
|
||||
/// <summary> Initializes a instance of the copri calls https object. </summary>
|
||||
/// <param name="copriHost">Host to connect to. </param>
|
||||
|
@ -49,7 +49,7 @@ namespace TINK.Repository
|
|||
}
|
||||
|
||||
/// <summary> Holds the URL for rest calls.</summary>
|
||||
private Uri m_oCopriHost;
|
||||
private readonly Uri m_oCopriHost;
|
||||
|
||||
/// <summary> Specifies name and version of app. </summary>
|
||||
private string UserAgent { get; }
|
||||
|
@ -84,9 +84,13 @@ namespace TINK.Repository
|
|||
=> await DoAuthoutAsync(m_oCopriHost.AbsoluteUri, requestBuilder.DoAuthout(), UserAgent);
|
||||
|
||||
/// <summary>Gets bikes available.</summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Response holding list of bikes.</returns>
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync()
|
||||
=> await GetBikesAvailableAsync(m_oCopriHost.AbsoluteUri, requestBuilder.GetBikesAvailable(), UserAgent);
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync(Uri operatorUri = null)
|
||||
=> await GetBikesAvailableAsync(
|
||||
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
|
||||
requestBuilder.GetBikesAvailable(),
|
||||
UserAgent);
|
||||
|
||||
/// <summary> Gets a list of bikes reserved/ booked by active user. </summary>
|
||||
/// <returns>Response holding list of bikes.</returns>
|
||||
|
|
|
@ -1326,13 +1326,14 @@ namespace TINK.Repository
|
|||
return await Task.Run(() => DoAuthout(SessionCookie));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets list of bikes from memory.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync()
|
||||
/// <summary>
|
||||
/// Gets list of bikes from memory.
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync(Uri operatorUri = null)
|
||||
{
|
||||
return await Task.Run(() => GetBikesAvailable(null, SessionCookie, ActiveSampleSet, ActiveStageIndex));
|
||||
return await Task.Run(() => GetBikesAvailable(null, SessionCookie, operatorUri, ActiveSampleSet, ActiveStageIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1487,18 +1488,20 @@ namespace TINK.Repository
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets list of bikes from memory.
|
||||
/// </summary>
|
||||
/// <param name="merchantId">Id of the merchant.</param>
|
||||
/// <param name="sessionCookie">Auto cookie of user if user is logged in.</param>
|
||||
/// <param name="sampleSet">Set of samples.</param>
|
||||
/// <param name="stageIndex">Index of the stage.</param>
|
||||
/// <returns></returns>
|
||||
public static BikesAvailableResponse GetBikesAvailable(
|
||||
/// <summary>
|
||||
/// Gets list of bikes from memory.
|
||||
/// </summary>
|
||||
/// <param name="merchantId">Id of the merchant.</param>
|
||||
/// <param name="sessionCookie">Auto cookie of user if user is logged in.</param>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <param name="sampleSet">Set of samples.</param>
|
||||
/// <param name="stageIndex">Index of the stage.</param>
|
||||
/// <returns></returns>
|
||||
public static BikesAvailableResponse GetBikesAvailable(
|
||||
string merchantId,
|
||||
string sessionCookie = null,
|
||||
SampleSets sampleSet = DEFAULT_SAMPLE_SET,
|
||||
Uri operatorUri = null,
|
||||
SampleSets sampleSet = DEFAULT_SAMPLE_SET,
|
||||
long stageIndex = DEFAULT_STAGE_INDEX)
|
||||
{
|
||||
switch (sampleSet)
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace TINK.Repository
|
|||
{
|
||||
public class CopriCallsMonkeyStore : ICopriCache
|
||||
{
|
||||
/// <summary> Prevents concurrent communictation. </summary>
|
||||
/// <summary> Prevents concurrent communication. </summary>
|
||||
private object monkeyLock = new object();
|
||||
|
||||
/// <summary> Builds requests.</summary>
|
||||
|
@ -235,14 +235,15 @@ namespace TINK.Repository
|
|||
throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
}
|
||||
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync()
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync(Uri operatorUri = null)
|
||||
{
|
||||
var l_oBikesAvailableTask = new TaskCompletionSource<BikesAvailableResponse>();
|
||||
var bikesAvailableTask = new TaskCompletionSource<BikesAvailableResponse>();
|
||||
lock (monkeyLock)
|
||||
{
|
||||
l_oBikesAvailableTask.SetResult(Barrel.Current.Get<BikesAvailableResponse>(requestBuilder.GetBikesAvailable()));
|
||||
bikesAvailableTask.SetResult(Barrel.Current.Get<BikesAvailableResponse>($"{operatorUri?.AbsoluteUri ?? string.Empty}{requestBuilder.GetBikesAvailable()}"));
|
||||
}
|
||||
return await l_oBikesAvailableTask.Task;
|
||||
return await bikesAvailableTask.Task;
|
||||
}
|
||||
|
||||
public async Task<BikesReservedOccupiedResponse> GetBikesOccupiedAsync()
|
||||
|
@ -322,21 +323,21 @@ namespace TINK.Repository
|
|||
}
|
||||
|
||||
/// <summary> Adds a bikes response to cache.</summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <param name="bikes">Bikes to add.</param>
|
||||
public void AddToCache(BikesAvailableResponse bikes)
|
||||
{
|
||||
AddToCache(bikes, ExpiresAfter);
|
||||
}
|
||||
public void AddToCache(BikesAvailableResponse bikes, Uri operatorUri = null)
|
||||
=> AddToCache(bikes, ExpiresAfter, operatorUri);
|
||||
|
||||
/// <summary> Adds a bikes response to cache.</summary>
|
||||
/// <param name="bikes">Bikes to add.</param>
|
||||
/// <param name="expiresAfter">Time after which anser is considered to be expired.</param>
|
||||
private void AddToCache(BikesAvailableResponse bikes, TimeSpan expiresAfter)
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <param name="expiresAfter">Time after which answer is considered to be expired.</param>
|
||||
private void AddToCache(BikesAvailableResponse bikes, TimeSpan expiresAfter, Uri operatorUri = null)
|
||||
{
|
||||
lock (monkeyLock)
|
||||
{
|
||||
Barrel.Current.Add(
|
||||
requestBuilder.GetBikesAvailable(),
|
||||
$"{operatorUri?.AbsoluteUri ?? string.Empty}{requestBuilder.GetBikesAvailable()}",
|
||||
JsonConvertRethrow.SerializeObject(bikes),
|
||||
expiresAfter);
|
||||
}
|
||||
|
|
|
@ -159,8 +159,9 @@ namespace TINK.Repository
|
|||
Task<StationsAvailableResponse> GetStationsAsync();
|
||||
|
||||
/// <summary> Gets a list of bikes from Copri. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Response holding list of bikes.</returns>
|
||||
Task<BikesAvailableResponse> GetBikesAvailableAsync();
|
||||
Task<BikesAvailableResponse> GetBikesAvailableAsync(Uri operatorUri = null);
|
||||
|
||||
/// <summary> Gets a list of bikes reserved/ booked by active user from Copri.</summary>
|
||||
/// <returns>Response holding list of bikes.</returns>
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace TINK.Repository.Response
|
|||
/// Textual description of response.
|
||||
/// </summary>
|
||||
/// <returns>Object as text.</returns>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Bike {bike}{(station != null ? $", at station {station}" : string.Empty)}{(!string.IsNullOrEmpty(description) ? $", {description}" : string.Empty)}{(!string.IsNullOrEmpty(state) ? $", status={state}" : string.Empty)}.";
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TINK.Repository.Response
|
||||
|
@ -10,9 +10,15 @@ namespace TINK.Repository.Response
|
|||
public class BikesAvailableResponse : ResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary of bikes.
|
||||
/// Dictionary of bikes available.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public Dictionary<string, BikeInfoAvailable> bikes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of bikes reserved or booked.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public Dictionary<string, BikeInfoReservedOrBooked> bikes_occupied { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TINK.Repository.Response
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TINK.Repository.Response
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ namespace TINK.Repository.Response
|
|||
public string tariff_info_html { get; private set; }
|
||||
|
||||
/// <summary> Textual description of response. </summary>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Response state is \"{response_state ?? string.Empty}\", " +
|
||||
$"auth cookie is \"{authcookie ?? string.Empty}\" and response is \"{response_text ?? string.Empty}\", " +
|
||||
|
|
|
@ -41,5 +41,11 @@ namespace TINK.Repository.Response.Stations.Station
|
|||
/// </summary>
|
||||
[DataMember]
|
||||
public Dictionary<string, BikeGroup> station_type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the uri of the operator which manages the bikes at station/ the station.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string uri_operator { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,8 +54,11 @@ namespace TINK.Model.Services.CopriApi
|
|||
}
|
||||
|
||||
/// <summary>Gets bikes available.</summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Response holding list of bikes.</returns>
|
||||
public async Task<Result<BikesAvailableResponse>> GetBikesAvailable(bool fromCache = false)
|
||||
public async Task<Result<BikesAvailableResponse>> GetBikesAvailable(
|
||||
bool fromCache = false,
|
||||
Uri operatorUri = null)
|
||||
{
|
||||
Log.ForContext<CopriProviderHttps>().Debug($"Request to get bikes available{(fromCache ? " from cache" : "")}...");
|
||||
if (!CacheServer.IsBikesAvailableExpired
|
||||
|
@ -63,14 +66,14 @@ namespace TINK.Model.Services.CopriApi
|
|||
{
|
||||
// No need to query because previous answer is not yet outdated.
|
||||
Log.ForContext<CopriProviderHttps>().Debug($"Returning bikes available from cache.");
|
||||
var bikesAvailableResponse = await CacheServer.GetBikesAvailableAsync();
|
||||
var bikesAvailableResponse = await CacheServer.GetBikesAvailableAsync(operatorUri);
|
||||
return new Result<BikesAvailableResponse>(typeof(CopriCallsMonkeyStore), bikesAvailableResponse, bikesAvailableResponse.GetGeneralData());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Log.ForContext<CopriProviderHttps>().Debug($"Querying bikes available from copri.");
|
||||
var bikesAvailableResponse = await HttpsServer.GetBikesAvailableAsync();
|
||||
var bikesAvailableResponse = await HttpsServer.GetBikesAvailableAsync(operatorUri);
|
||||
return new Result<BikesAvailableResponse>(
|
||||
typeof(CopriCallsHttps),
|
||||
bikesAvailableResponse.GetIsResponseOk(MultilingualResources.AppResources.ErrorBikesAvailableResponseNotOk),
|
||||
|
@ -80,7 +83,7 @@ namespace TINK.Model.Services.CopriApi
|
|||
{
|
||||
// Return response from cache.
|
||||
Log.ForContext<CopriProviderHttps>().Debug("An error occurred querying bikes available. {Exception}.", exception);
|
||||
var bikesAvailableResponse = await CacheServer.GetBikesAvailableAsync();
|
||||
var bikesAvailableResponse = await CacheServer.GetBikesAvailableAsync(operatorUri);
|
||||
return new Result<BikesAvailableResponse>(typeof(CopriCallsMonkeyStore), bikesAvailableResponse, bikesAvailableResponse.GetGeneralData(), exception);
|
||||
}
|
||||
}
|
||||
|
@ -171,9 +174,10 @@ namespace TINK.Model.Services.CopriApi
|
|||
}
|
||||
|
||||
/// <summary>Adds https--response to cache if response is ok. </summary>
|
||||
/// <param name="response">Response to add to cache.</param>
|
||||
/// <param name="result">Response to add to cache.</param>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns></returns>
|
||||
public void AddToCache(Result<BikesAvailableResponse> result)
|
||||
public void AddToCache(Result<BikesAvailableResponse> result, Uri operatorUri = null)
|
||||
{
|
||||
Log.ForContext<CopriProviderHttps>().Debug($"Request to add bikes available response to cache...");
|
||||
if (result.Source == typeof(CopriCallsMonkeyStore)
|
||||
|
@ -184,7 +188,7 @@ namespace TINK.Model.Services.CopriApi
|
|||
}
|
||||
|
||||
Log.ForContext<CopriProviderHttps>().Debug($"Add bikes available response to cache.");
|
||||
CacheServer.AddToCache(result.Response);
|
||||
CacheServer.AddToCache(result.Response, operatorUri);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -108,8 +108,9 @@ namespace TINK.Model.Services.CopriApi
|
|||
public async Task<AuthorizationoutResponse> DoAuthoutAsync()
|
||||
=> await monkeyStore.DoAuthoutAsync();
|
||||
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync()
|
||||
=> await monkeyStore.GetBikesAvailableAsync();
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync(Uri operatorUri = null)
|
||||
=> await monkeyStore.GetBikesAvailableAsync(operatorUri);
|
||||
|
||||
public async Task<BikesReservedOccupiedResponse> GetBikesOccupiedAsync()
|
||||
=> await monkeyStore.GetBikesOccupiedAsync();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Repository;
|
||||
using TINK.Repository.Response;
|
||||
|
@ -14,8 +15,9 @@ namespace TINK.Model.Services.CopriApi
|
|||
Task<Result<StationsAvailableResponse>> GetStations(bool fromCache = false);
|
||||
|
||||
/// <summary> Gets a list of bikes from Copri. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <returns>Response holding list of bikes.</returns>
|
||||
Task<Result<BikesAvailableResponse>> GetBikesAvailable(bool fromCache = false);
|
||||
Task<Result<BikesAvailableResponse>> GetBikesAvailable(bool fromCache = false, Uri operatorUri = null);
|
||||
|
||||
/// <summary> Gets a list of bikes reserved/ booked by active user from Copri.</summary>
|
||||
/// <returns>Response holding list of bikes.</returns>
|
||||
|
@ -26,8 +28,9 @@ namespace TINK.Model.Services.CopriApi
|
|||
void AddToCache(Result<StationsAvailableResponse> result);
|
||||
|
||||
/// <summary>Adds https--response to cache if response is ok. </summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <param name="response">Response to add to cache.</param>
|
||||
void AddToCache(Result<BikesAvailableResponse> result);
|
||||
void AddToCache(Result<BikesAvailableResponse> result, Uri operatorUri = null);
|
||||
|
||||
/// <summary>Adds https--response to cache if response is ok. </summary>
|
||||
/// <param name="response">Response to add to cache.</param>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using TINK.Repository;
|
||||
using System;
|
||||
using TINK.Repository;
|
||||
using TINK.Repository.Response;
|
||||
using TINK.Repository.Response.Stations;
|
||||
|
||||
|
@ -17,8 +18,9 @@ namespace TINK.Model.Services.CopriApi
|
|||
bool IsBikesAvailableExpired { get; }
|
||||
|
||||
/// <summary> Adds a bikes response to cache.</summary>
|
||||
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
|
||||
/// <param name="bikes">Bikes to add.</param>
|
||||
void AddToCache(BikesAvailableResponse bikes);
|
||||
void AddToCache(BikesAvailableResponse bikes, Uri operatorUri = null);
|
||||
|
||||
/// <summary> Gets a value indicating whether stations are expired or not.</summary>
|
||||
bool IsBikesOccupiedExpired { get; }
|
||||
|
|
52
TINKLib/ShareeBikeLibCore.csproj
Normal file
52
TINKLib/ShareeBikeLibCore.csproj
Normal file
|
@ -0,0 +1,52 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Model\Message\Message.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Model\Message\Message.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MonkeyCache" Version="1.6.3" />
|
||||
<PackageReference Include="MonkeyCache.FileStore" Version="1.6.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Plugin.BLE" Version="2.1.3" />
|
||||
<PackageReference Include="Plugin.BluetoothLE" Version="6.3.0.19" />
|
||||
<PackageReference Include="Plugin.Permissions" Version="6.0.1" />
|
||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="System.Collections" Version="4.3.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Net.Primitives" Version="4.3.1" />
|
||||
<PackageReference Include="System.Net.Sockets" Version="4.3.0" />
|
||||
<PackageReference Include="System.Private.DataContractSerialization" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Xam.Plugin.Connectivity" Version="3.2.0" />
|
||||
<PackageReference Include="Xam.Plugins.Messaging" Version="5.2.0" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.8.0" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2612" />
|
||||
<PackageReference Include="Xamarin.Forms.GoogleMaps" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LockItBLE\LockItBLECore.csproj" />
|
||||
<ProjectReference Include="..\LockItShared\LockItSharedCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="TestShareeBikeLibCore" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
125
TINKLib/ShareeBikeLibCore.sln
Normal file
125
TINKLib/ShareeBikeLibCore.sln
Normal file
|
@ -0,0 +1,125 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.6.33815.320
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{49C8F824-4752-449E-A53C-35A2722AFA99}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set02 - Book 3rd bike", "Set02 - Book 3rd bike", "{50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set02\Story.json = ..\TestData\Set02\Story.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{A5D8D93B-4D4E-4C4C-A70C-44A451D6C722}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set02\001\bikes_available.json = ..\TestData\Set02\001\bikes_available.json
|
||||
..\TestData\Set02\001\booking_request_bike_8.json = ..\TestData\Set02\001\booking_request_bike_8.json
|
||||
..\TestData\Set02\001\stations_all.json = ..\TestData\Set02\001\stations_all.json
|
||||
..\TestData\Set02\001\user_bikes_occupied.json = ..\TestData\Set02\001\user_bikes_occupied.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "002", "002", "{D0A2A88B-5DD9-4B40-A8AE-2AED6FB41911}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set02\002\bikes_available.json = ..\TestData\Set02\002\bikes_available.json
|
||||
..\TestData\Set02\002\stations_all.json = ..\TestData\Set02\002\stations_all.json
|
||||
..\TestData\Set02\002\user_bikes_occupied.json = ..\TestData\Set02\002\user_bikes_occupied.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "003", "003", "{272FB1A5-BBC5-41DF-91C2-C1C1A85AFD44}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set02\003\bikes_available.json = ..\TestData\Set02\003\bikes_available.json
|
||||
..\TestData\Set02\003\stations_all.json = ..\TestData\Set02\003\stations_all.json
|
||||
..\TestData\Set02\003\user_bikes_occupied.json = ..\TestData\Set02\003\user_bikes_occupied.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set03 - Book 1st bike", "Set03 - Book 1st bike", "{E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set03\Story.json = ..\TestData\Set03\Story.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{B0BE116C-B05C-4864-9D1F-5CE1A8EC7BD0}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set03\001\booking_request_bike_20.json = ..\TestData\Set03\001\booking_request_bike_20.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "002", "002", "{C579CA91-17DC-4AC4-8F1B-377A245883FD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set01 - Log in", "Set01 - Log in", "{A872956F-17F0-416E-9A9A-F28D96F13A94}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{104F18F2-1261-42C0-96A4-F5BBACF595DA}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set01\001\authorization_javaminister%40gmail.com_javaminister_HwId1000000000000.json = ..\TestData\Set01\001\authorization_javaminister%40gmail.com_javaminister_HwId1000000000000.json
|
||||
..\TestData\Set01\001\bikes_available.json = ..\TestData\Set01\001\bikes_available.json
|
||||
..\TestData\Set01\001\bikes_occupied.json = ..\TestData\Set01\001\bikes_occupied.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set04 - Cancel Booking", "Set04 - Cancel Booking", "{55C5EF2A-FDEB-40F6-ABE9-84B7B1981B2B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{AADA3B61-8626-43AE-BED9-BA5AA3D93576}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\TestData\Set04 - Cancel Booking\001\booking_cancel_bike_7.json = ..\TestData\Set04 - Cancel Booking\001\booking_cancel_bike_7.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShareeBikeLibCore", "ShareeBikeLibCore.csproj", "{197A5D17-8EE0-4AC1-A977-A0F238ADB9E2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LockItSharedCore", "..\LockItShared\LockItSharedCore.csproj", "{A48B2C74-1DFC-4CCE-AB90-D2796ED8B228}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LockItBLECore", "..\LockItBLE\LockItBLECore.csproj", "{9F916E1C-7F6A-4D11-85AA-56FC889722AC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFrameworkCore", "..\TestFramework\TestFrameworkCore.csproj", "{04E48112-AA27-4702-924B-30F08E6CC2C6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestShareeBikeLibCore", "..\TestShareeLib\TestShareeBikeLibCore.csproj", "{356ED049-D8C7-42D1-BD67-A28F39AFED5D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{197A5D17-8EE0-4AC1-A977-A0F238ADB9E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{197A5D17-8EE0-4AC1-A977-A0F238ADB9E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{197A5D17-8EE0-4AC1-A977-A0F238ADB9E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{197A5D17-8EE0-4AC1-A977-A0F238ADB9E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A48B2C74-1DFC-4CCE-AB90-D2796ED8B228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A48B2C74-1DFC-4CCE-AB90-D2796ED8B228}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A48B2C74-1DFC-4CCE-AB90-D2796ED8B228}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A48B2C74-1DFC-4CCE-AB90-D2796ED8B228}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9F916E1C-7F6A-4D11-85AA-56FC889722AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9F916E1C-7F6A-4D11-85AA-56FC889722AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9F916E1C-7F6A-4D11-85AA-56FC889722AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9F916E1C-7F6A-4D11-85AA-56FC889722AC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{04E48112-AA27-4702-924B-30F08E6CC2C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{04E48112-AA27-4702-924B-30F08E6CC2C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04E48112-AA27-4702-924B-30F08E6CC2C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04E48112-AA27-4702-924B-30F08E6CC2C6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{356ED049-D8C7-42D1-BD67-A28F39AFED5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{356ED049-D8C7-42D1-BD67-A28F39AFED5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{356ED049-D8C7-42D1-BD67-A28F39AFED5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{356ED049-D8C7-42D1-BD67-A28F39AFED5D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0} = {49C8F824-4752-449E-A53C-35A2722AFA99}
|
||||
{A5D8D93B-4D4E-4C4C-A70C-44A451D6C722} = {50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}
|
||||
{D0A2A88B-5DD9-4B40-A8AE-2AED6FB41911} = {50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}
|
||||
{272FB1A5-BBC5-41DF-91C2-C1C1A85AFD44} = {50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}
|
||||
{E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61} = {49C8F824-4752-449E-A53C-35A2722AFA99}
|
||||
{B0BE116C-B05C-4864-9D1F-5CE1A8EC7BD0} = {E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61}
|
||||
{C579CA91-17DC-4AC4-8F1B-377A245883FD} = {E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61}
|
||||
{A872956F-17F0-416E-9A9A-F28D96F13A94} = {49C8F824-4752-449E-A53C-35A2722AFA99}
|
||||
{104F18F2-1261-42C0-96A4-F5BBACF595DA} = {A872956F-17F0-416E-9A9A-F28D96F13A94}
|
||||
{55C5EF2A-FDEB-40F6-ABE9-84B7B1981B2B} = {49C8F824-4752-449E-A53C-35A2722AFA99}
|
||||
{AADA3B61-8626-43AE-BED9-BA5AA3D93576} = {55C5EF2A-FDEB-40F6-ABE9-84B7B1981B2B}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C6529CD7-C3F7-4E80-89B5-002E2B8E3EB5}
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
Policies = $0
|
||||
$0.DotNetNamingPolicy = $1
|
||||
$1.DirectoryNamespaceAssociation = PrefixedHierarchical
|
||||
version = 3.0
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -59,7 +59,6 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="Services\Permissions\Essentials\" />
|
||||
<Folder Include="Services\Permissions\Plugin\" />
|
||||
<Folder Include="ViewModel\Info\BikeInfo\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Model\Bikes\BikeInfoNS\CopriLock\ILockInfoMutable.cs" />
|
||||
|
@ -100,4 +99,7 @@
|
|||
<HintPath>..\..\..\..\..\..\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\Xamarin.iOS\v1.0\Xamarin.iOS.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -64,6 +64,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{AADA3B61-862
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestShareeLib", "..\TestShareeLib\TestShareeLib.csproj", "{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LockItShared", "..\LockItShared\LockItShared.csproj", "{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LockItBLE", "..\LockItBLE\LockItBLE.csproj", "{9B4CA43E-5059-41D4-B9C7-F4C04169B332}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFramework", "..\TestFramework\TestFramework.csproj", "{7051CB77-329C-4ACA-9CBE-7208528F17D8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||
|
@ -188,6 +194,150 @@ Global
|
|||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0F911785-7DF5-4C91-93D6-FBD4B4A6F555}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9B4CA43E-5059-41D4-B9C7-F4C04169B332}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7051CB77-329C-4ACA-9CBE-7208528F17D8}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -51,19 +51,12 @@ namespace TINK.View
|
|||
/// <returns>T</returns>
|
||||
Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons);
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary> Shows a page.</summary>
|
||||
/// <param name="type">Type of page to show.</param>
|
||||
/// <param name="title">Title of page to show.</param>
|
||||
void ShowPage(ViewTypes type, string title = null);
|
||||
#else
|
||||
/// <summary> Shows a page.</summary>
|
||||
/// <param name="route">Route of the page to show.</param>
|
||||
#if USCSHARP9
|
||||
public Task ShowPage(string route);
|
||||
#else
|
||||
Task ShowPage(string route);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <summary> Pushes a page onto the stack. </summary>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
#if USEFLYOUT
|
||||
using Serilog;
|
||||
using System;
|
||||
|
||||
namespace TINK.View.MasterDetail
|
||||
{
|
||||
|
||||
public class EmptyNavigationMasterDetail : INavigationMasterDetail
|
||||
{
|
||||
public bool IsGestureEnabled { set => Log.ForContext<EmptyNavigationMasterDetail>().Error($"Unexpected call of {nameof(IsGestureEnabled)} detected."); }
|
||||
|
||||
public void ShowPage(Type p_oTypeOfPage, string p_strTitle = null)
|
||||
{
|
||||
Log.ForContext<EmptyNavigationMasterDetail>().Error($"Unexpected call of {nameof(ShowPage)} detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,17 +0,0 @@
|
|||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
|
||||
namespace TINK.View
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to provide navigation functionality to detail page.
|
||||
/// </summary>
|
||||
public interface IDetailPage
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate to perform navigation.
|
||||
/// </summary>
|
||||
INavigationMasterDetail NavigationMasterDetail { set; }
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,20 +0,0 @@
|
|||
#if USEFLYOUT
|
||||
using System;
|
||||
|
||||
namespace TINK.View.MasterDetail
|
||||
{
|
||||
public interface INavigationMasterDetail
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates and a page an shows it.
|
||||
/// </summary>
|
||||
/// <param name="p_oTypeOfPage">Type of page to show.</param>
|
||||
void ShowPage(Type p_oTypeOfPage, string p_strTitle = null);
|
||||
|
||||
/// <summary>
|
||||
/// Set a value indicating whether master deatail navigation menu is available or not.
|
||||
/// </summary>
|
||||
bool IsGestureEnabled { set; }
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -377,11 +377,7 @@ namespace TINK.ViewModel.Account
|
|||
try
|
||||
{
|
||||
// Switch to map view after log out.
|
||||
#if USEFLYOUT
|
||||
m_oViewService.ShowPage(ViewTypes.MapPage);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
/// <summary>
|
||||
/// Gets the name of the button when bike is cancel reservation.
|
||||
/// </summary>
|
||||
public string ButtonText => AppResources.ActionReturn; // "Miete beenden"
|
||||
public string ButtonText => AppResources.ActionEndRental; // "Miete beenden"
|
||||
|
||||
/// <summary>
|
||||
/// Reference on view service to show modal notifications and to perform navigation.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.State;
|
||||
|
@ -66,11 +66,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
try
|
||||
{
|
||||
// Switch to login page
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
#endif
|
||||
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(selectedBike, AppResources.ActionCancelRequest, true, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
|
||||
IUser activeUser) : base(selectedBike, AppResources.ActionCancelReservation, true, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ using System.ComponentModel;
|
|||
using System.Text.RegularExpressions;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
|
||||
using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS;
|
||||
#if !USEFLYOUT
|
||||
#endif
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
|
|
|
@ -153,7 +153,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task CloseLockAsync()
|
||||
{
|
||||
Log.ForContext<T>().Information("User request to lock bike {bike}.", SelectedBike);
|
||||
Log.ForContext<T>().Information("User request to close lock of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// lock GUI
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
@ -187,10 +187,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
#else
|
||||
await SelectedBike.CloseLockAsync(this, stopPollingTask);
|
||||
#endif
|
||||
Log.ForContext<T>().Information("User locked {bike} successfully.", SelectedBike);
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} closed successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} can not be closed. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
|
@ -222,6 +223,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
// Message for parking bike
|
||||
if(IsEndRentalRequested == false)
|
||||
{
|
||||
Log.ForContext<T>().Information("User request to park bike {bikeId}.", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentalProcessCloseLockFinishedTitle,
|
||||
AppResources.MessageRentalProcessCloseLockFinishedText,
|
||||
|
|
|
@ -9,6 +9,7 @@ using TINK.View;
|
|||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand;
|
||||
using Serilog;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||
{
|
||||
|
@ -144,7 +145,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
/// <summary> Return bike. </summary>
|
||||
public async Task EndRentalAsync()
|
||||
{
|
||||
Log.ForContext<T>().Information("User requests to return bike {bike}.", SelectedBike);
|
||||
Log.ForContext<T>().Information("User request to end rental of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// lock GUI
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
@ -170,9 +171,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
try
|
||||
{
|
||||
currentLocationDto = await SelectedBike.GetLockedBikeLocationAsync(this);
|
||||
Log.ForContext<T>().Information("Location information for lock received successfully.");
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Information("Location information for lock can not be received. {@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
|
@ -192,16 +195,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
currentLocationDto);
|
||||
Log.ForContext<T>().Information("Rental of bike {bikeId} was terminated successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Information("Rental of bike {bikeId} can not be terminated.", SelectedBike.Id);
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<T>().Information("User selected booked bike {bike} but returning failed (Copri server not reachable).", SelectedBike);
|
||||
// No web.
|
||||
Log.ForContext<T>().Error("Copri server not reachable. No web.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -210,11 +215,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<T>().Information(
|
||||
"User selected booked bike {bike} but returning failed. COPRI returned out of GEO fencing error. Position send to COPRI {@position}.",
|
||||
SelectedBike,
|
||||
currentLocationDto);
|
||||
// not at station.
|
||||
Log.ForContext<T>().Error("COPRI returned out of GEO fencing error. Position send to COPRI {position}.", currentLocationDto);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -223,8 +225,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<T>().Information("User selected booked bike {bike} but returning failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
// no GPS data.
|
||||
Log.ForContext<T>().Error("COPRI returned a no-GPS-data error.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -233,8 +235,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<T>().Information("User selected booked bike {bike} but returning failed. COPRI returned an error.", SelectedBike);
|
||||
// COPRI exception.
|
||||
Log.ForContext<T>().Error("COPRI returned an error. {response}", copriException.Response);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -244,7 +246,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<T>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -304,19 +306,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
Message = feedback.Message
|
||||
},
|
||||
feedBackUri);
|
||||
Log.ForContext<T>().Information("Feedback for bike {bikeId} was submitted successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<T>().Information("Feedback for bike {bikeId} can not be submitted.", SelectedBike.Id);
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<T>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
// Copri exception.
|
||||
Log.ForContext<T>().Debug("COPRI returned an error. {response}", copriException.Response);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<T>().Debug("{@exception}", exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
|
@ -332,10 +335,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
|
@ -344,7 +348,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Confirmation message that rental is ended
|
||||
Log.ForContext<T>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
Log.ForContext<T>().Information("Rental of bike {bikeId} was terminated successfully.", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
String.Format(AppResources.MessageRentalProcessEndRentalFinishedTitle, SelectedBike.Id),
|
||||
|
|
|
@ -32,8 +32,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionReturn, // Copri button text "Miete beenden"
|
||||
true, // Show button to enabled returning of bike.
|
||||
AppResources.ActionEndRental, // End Rental
|
||||
true, // Show button "End Rental"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -44,8 +44,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndPause;
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
LockitButtonText = AppResources.ActionOpenLock; // Open Lock
|
||||
IsLockitButtonVisible = true; // Show button "Open Lock"
|
||||
|
||||
_endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
|
||||
selectedBike,
|
||||
|
@ -98,28 +98,28 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedClosed>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
Log.ForContext<BookedClosed>().Information("User request to unlock booked bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// open lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<BookedClosed>().Information("Lock of bike {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<BookedClosed>().Information("Lock of bike {bikeId} can not be opened.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -128,7 +128,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -137,7 +137,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -147,7 +147,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -156,7 +156,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -171,36 +171,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedClosed>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Information("Battery state of lock from {bikeId} can not be read. ", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedClosed>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -212,42 +207,42 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<BookedClosed>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<BookedClosed>().Debug("{response}.", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<BookedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedClosed>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true;
|
||||
LockitButtonText = AppResources.ActionSearchLock; // Search Lock
|
||||
IsLockitButtonVisible = true; // show button "Search Lock"
|
||||
}
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
@ -69,7 +69,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedDisconnected>().Information("Request to search {bike} detected.", SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Information("User request to connect to lock of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
@ -81,16 +81,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Repeat booking to get a new seed/ k_user value.
|
||||
await ConnectorFactory(IsConnected).Command.CalculateAuthKeys(SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Information("Calculation of AuthKeys successfully.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Calculation of AuthKeys failed.");
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedDisconnected>().Information("User selected booked bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<BookedDisconnected>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -99,7 +101,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Error("User selected booked bike {l_oId} to connect to lock. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<BookedDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
|
@ -115,6 +117,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
// Reconnect to lock
|
||||
LockInfoTdo result = null;
|
||||
var continueConnect = true;
|
||||
var retryCount = 1;
|
||||
|
@ -127,15 +130,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
result = await LockService.ConnectAsync(
|
||||
new LockInfoAuthTdo.Builder { Id = SelectedBike.LockInfo.Id, Guid = SelectedBike.LockInfo.Guid, K_seed = SelectedBike.LockInfo.Seed, K_u = SelectedBike.LockInfo.UserKey }.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(retryCount));
|
||||
Log.ForContext<BookedDisconnected>().Information("Connected to lock of bike {bikeId} successfully. Value is {lockState}.", SelectedBike.Id, SelectedBike.LockInfo.State);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Information("Connection to lock of bike {bikeId} failed. ", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is ConnectBluetoothNotOnException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Bluetooth not on.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockBluetoothNotOn,
|
||||
|
@ -144,7 +148,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is ConnectLocationPermissionMissingException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Location permission missing.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorNoLocationPermission,
|
||||
|
@ -153,7 +157,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is ConnectLocationOffException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Location services not on.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockLocationOff,
|
||||
|
@ -162,7 +166,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is OutOfReachException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Lock is out of reach.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
|
@ -170,7 +174,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Error("Lock can not be found. {Exception}", exception);
|
||||
Log.ForContext<BookedDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
string message;
|
||||
if (retryCount < 2)
|
||||
|
@ -190,7 +194,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
message,
|
||||
"",
|
||||
string.Empty,
|
||||
AppResources.MessageAnswerRetry,
|
||||
AppResources.MessageAnswerCancel);
|
||||
}
|
||||
|
@ -212,7 +216,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
if (!(result?.State is LockitLockingState lockingState))
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Error("Locking state of bike {bikeId} not found.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
|
@ -228,11 +232,75 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
// get current locking state
|
||||
var state = lockingState.GetLockingState();
|
||||
SelectedBike.LockInfo.State = state;
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Information($"State for bike {SelectedBike.Id} updated successfully. Value is {SelectedBike.LockInfo.State}.");
|
||||
// get current lock charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<BookedDisconnected>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// get lock infos
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<BookedDisconnected>().Debug("Copri server not reachable, no web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<BookedDisconnected>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
|
|
|
@ -30,8 +30,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionClose, // Copri button text: "Close lock"
|
||||
true, // Show button to allow user to close lock.
|
||||
AppResources.ActionCloseLock, // Close Lock
|
||||
true, // Show button "Close Lock"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
|
|
@ -9,7 +9,6 @@ using TINK.Model.Device;
|
|||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
|
@ -35,8 +34,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndPause, // Schloss öffnen und Miete fortsetzen.
|
||||
true, // Show button to enabled returning of bike.
|
||||
AppResources.ActionOpenLock, // Open Lock
|
||||
true, // Show button "Open Lock"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -47,8 +46,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen".;
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
|
||||
IsLockitButtonVisible = true; // Show button "Close Lock"
|
||||
|
||||
_closeLockActionViewModel = new CloseLockActionViewModel<BookedOpen>(
|
||||
selectedBike,
|
||||
|
@ -69,34 +68,70 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
{
|
||||
await _closeLockActionViewModel.CloseLockAsync();
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
if (_closeLockActionViewModel.IsEndRentalRequested == false)
|
||||
{
|
||||
return Create(
|
||||
SelectedBike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
GeolocationService,
|
||||
LockService,
|
||||
ViewUpdateManager,
|
||||
SmartDevice,
|
||||
ViewService,
|
||||
BikesViewModel,
|
||||
ActiveUser);
|
||||
}
|
||||
|
||||
var _endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
|
||||
SelectedBike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
LockService,
|
||||
ViewUpdateManager,
|
||||
ViewService,
|
||||
BikesViewModel);
|
||||
|
||||
await _endRentalActionViewModel.EndRentalAsync();
|
||||
|
||||
return Create(
|
||||
SelectedBike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
GeolocationService,
|
||||
LockService,
|
||||
ViewUpdateManager,
|
||||
SmartDevice,
|
||||
ViewService,
|
||||
BikesViewModel,
|
||||
ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
Log.ForContext<BookedUnknown>().Information("User request to unlock bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// open lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<BookedUnknown>().Information("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock is out of reach");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -105,7 +140,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -114,7 +149,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -124,7 +159,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -133,7 +168,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -148,35 +183,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<BookedUnknown>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedUnknown>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -188,36 +219,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<BookedUnknown>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<BookedUnknown>().Information("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<BookedUnknown>().Information("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<BookedUnknown>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
|
|
@ -4,7 +4,6 @@ using Serilog;
|
|||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
|
@ -33,8 +32,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionRequest, // Copri text: "Rad reservieren"
|
||||
true, // Show copri button to enable reserving and opening
|
||||
AppResources.ActionReserveBike, // Reserve Bike
|
||||
true, // Show button "Reserve Bike"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -46,17 +45,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false; // If bike is not reserved/ booked app can not connect to lock
|
||||
IsLockitButtonVisible = false; // If bike is not reserved/ rented app can not connect to lock
|
||||
}
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReserveBookAndOpen();
|
||||
/// <summary>Reserve bike, connect to lock, open lock and rent bike.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReserveRentBikeAndOpenLock();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> ReserveBookAndOpen()
|
||||
/// <summary>Reserve and rent bike.</summary>
|
||||
public async Task<IRequestHandler> ReserveRentBikeAndOpenLock()
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("User request to reserve bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really reserve bike?
|
||||
|
@ -72,13 +72,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected centered bike {bike} in order to reserve but action was canceled.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User canceled request to reserve bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<DisposableDisconnected>().Information("Request to book and open lock for bike {bike} detected.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
|
@ -87,18 +86,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// reserve bike
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoReserve(SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User reserved bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("Request to reserve bike {bikeId} declined.", SelectedBike.Id);
|
||||
if (exception is BookingDeclinedException)
|
||||
{
|
||||
// Too many bikes booked.
|
||||
Log.ForContext<DisposableDisconnected>().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Maximum count of bikes {exception.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageHintTitle,
|
||||
|
@ -109,7 +110,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected centered bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -118,7 +119,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected centered bike {bike} but reserving failed. {@l_oException}", SelectedBike, exception);
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReservingBikeTitle,
|
||||
|
@ -143,19 +144,21 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
result = await LockService.ConnectAsync(
|
||||
new LockInfoAuthTdo.Builder { Id = SelectedBike.LockInfo.Id, Guid = SelectedBike.LockInfo.Guid, K_seed = SelectedBike.LockInfo.Seed, K_u = SelectedBike.LockInfo.UserKey }.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(1));
|
||||
Log.ForContext<DisposableDisconnected>().Information("Connected to lock of bike {bikeId} successfully. Value is {lockState}.", SelectedBike.Id, SelectedBike.LockInfo.State);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Connection to lock of bike {bikeId} failed.");
|
||||
|
||||
// Do not display any messages here, because search is implicit.
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock state can not be retrieved, lock is out of reach. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextLockIsOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock state can not be retrieved. {Exception}", exception);
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextLockNotFound;
|
||||
}
|
||||
|
||||
|
@ -167,11 +170,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current locking state
|
||||
SelectedBike.LockInfo.State = result?.State?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
if (SelectedBike.LockInfo.State == LockingState.UnknownDisconnected)
|
||||
{
|
||||
// Do not display any messages here, because search is implicit.
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock is still not connected.");
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
|
@ -182,32 +186,34 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock found {bike} successfully.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
alertResult = SelectedBike.LockInfo.State != LockingState.Open
|
||||
? await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo)
|
||||
: await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected recently requested bike {bike} in order to reserve but did deny to book bike.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User request to not book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableDisconnected>().Information("Disconnected from lock of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
|
@ -219,23 +225,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected recently requested bike {bike} in order to book.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User request to book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
}
|
||||
Log.ForContext<DisposableDisconnected>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
Log.ForContext<DisposableDisconnected>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected recently requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -244,11 +258,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -260,107 +274,96 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
// Unlock bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock can not be opened. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock is out of reach.");
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<DisposableDisconnected>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock is out of reach");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -372,36 +375,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<DisposableDisconnected>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<DisposableDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
|
|
@ -9,6 +9,7 @@ using TINK.MultilingualResources;
|
|||
using TINK.Repository.Exception;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.CopriApi.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
|
||||
|
@ -45,8 +46,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseOrBook,
|
||||
true, // Show copri button to enable reserving
|
||||
AppResources.ActionRentBike, // Rent Bike
|
||||
true, // Show button "Rent Bike"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -57,226 +58,362 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false;
|
||||
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
|
||||
IsLockitButtonVisible = true; // Show button "Close Lock"
|
||||
}
|
||||
|
||||
/// <summary>Books bike by reserving bike, opening lock and booking bike.</summary>
|
||||
/// <summary>Rents bike by reserving bike and renting bike.</summary>
|
||||
/// <returns>Next request handler.</returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockOrDoBook();
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await RentBike();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
|
||||
/// <summary>Books bike by reserving bike, opening lock and booking bike.</summary>
|
||||
/// <summary>Closes Lock.</summary>
|
||||
/// <returns>Next request handler.</returns>
|
||||
public async Task<IRequestHandler> CloseLockOrDoBook()
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
public async Task<IRequestHandler> RentBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<DisposableOpen>().Information("User request to book bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Ask whether to really book bike or close lock?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
String.Format(AppResources.QuestionCloseOrBook, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.ActionBook,
|
||||
AppResources.ActionClose);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// Close lock
|
||||
Log.ForContext<DisposableOpen>().Information("User selected disposable bike {bike} in order to close lock.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoltBlockedException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<DisposableOpen>().Information("Request to book bike {bike}.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify copri about unlock action in order to start booking.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
//reserve bike prior to book.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
await ConnectorFactory(IsConnected).Command.DoReserve(SelectedBike);
|
||||
Log.ForContext<DisposableOpen>().Information("User reserved bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
Log.ForContext<DisposableOpen>().Information("Request to reserve bike {bikeId} declined.", SelectedBike.Id);
|
||||
if (exception is BookingDeclinedException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableOpen>().Information("User selected requested bike {l_oId} but reserving failed (Copri server not reachable).", SelectedBike.Id);
|
||||
// Too many bikes booked.
|
||||
Log.ForContext<DisposableOpen>().Error("Maximum count of bikes {exception.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.MessageHintTitle,
|
||||
string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableOpen>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<DisposableOpen>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
string.Format(l_oException.Message, AppResources.ErrorTryAgain),
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReservingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// If booking failed lock bike again because bike is only reserved.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Locking bike after booking failure failed. {Exception}", exception);
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableOpen>().Information("Lock from bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableOpen>().Information("Lock from bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
// Update status text and unlock list of bikes because no more action is pending.
|
||||
BikesViewModel.ActionText = string.Empty; // Todo: Move this statement in front of finally block because in catch block BikesViewModel.ActionText is already set to empty.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Book bike
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
Log.ForContext<DisposableOpen>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<DisposableOpen>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableOpen>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableOpen>().Error("Lock from bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock from bike {bikeId} can not be disconnected. {Exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableOpen>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
// Get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<DisposableOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// Update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<DisposableOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<DisposableOpen>().Debug("Copri server not reachable. No web");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<DisposableOpen>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
// Update status text and unlock list of bikes because no more action is pending.
|
||||
BikesViewModel.ActionText = string.Empty; // Todo: Move this statement in front of finally block because in catch block BikesViewModel.ActionText is already set to empty.
|
||||
BikesViewModel.IsIdle = true;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Request is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
Log.ForContext<DisposableOpen>().Information("User request to close lock of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<DisposableOpen>().Information("Lock of bike {bikeId} closed successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Information("Lock of bike {bikeId} can not be closed.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock is moving.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoltBlockedException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} can not be read", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedOpen>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableOpen>().Error("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,11 +64,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
try
|
||||
{
|
||||
// Switch to map page
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
|
|
@ -38,8 +38,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCancelRequest, // Copri button text: "Reservierung abbrechen"
|
||||
true, // Show button to enable canceling reservation.
|
||||
AppResources.ActionCancelReservation, // Cancel Reservation
|
||||
true, // Show button "Cancel Reservation"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -50,37 +50,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndBook; // Button text: "Schloss öffnen & Rad mieten"
|
||||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
LockitButtonText = AppResources.ActionOpenLockAndRentBike; // Open Lock & Rent Bike
|
||||
IsLockitButtonVisible = true; // Show button "Open Lock & Rent Bike"
|
||||
}
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
/// <summary> Open lock and book bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLockAndDoBook();
|
||||
/// <summary> Open lock and rent bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLockAndRentBike();
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> CancelReservation()
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("User request to cancel reservation of bike {bikeId}", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
if (result == false)
|
||||
{
|
||||
// User aborted cancel process
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation but action was canceled.", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Information("User canceled request to cancel reservation of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
@ -90,15 +89,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("User canceled reservation of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("Canceling reservation of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Error("Invalid auth. response.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
|
@ -109,7 +109,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BikesViewModel>().Information("User selected reserved bike {l_oId} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Error("Copri server not reachable.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
|
@ -117,7 +117,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedClosed>().Error("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
|
@ -132,18 +132,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BikesViewModel>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
|
@ -154,9 +152,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Open lock and book bike. </summary>
|
||||
public async Task<IRequestHandler> OpenLockAndDoBook()
|
||||
/// <summary> Open lock and rent bike. </summary>
|
||||
public async Task<IRequestHandler> OpenLockAndRentBike()
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("User request to book and open bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
|
@ -169,13 +168,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {bike} in order to book but action was canceled.", SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("User canceled request to book bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {bike} in order to book but action was canceled.", SelectedBike);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
@ -186,15 +183,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
Log.ForContext<ReservedClosed>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
Log.ForContext<ReservedClosed>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
|
@ -203,12 +201,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<ReservedClosed>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -221,18 +219,19 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Unlock bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from {bikeId} opened successfully.",SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -242,7 +241,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -252,7 +251,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -263,7 +262,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -273,7 +272,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
|
@ -285,47 +284,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedClosed>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -337,36 +320,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<ReservedClosed>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedClosed>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
|
|
@ -32,8 +32,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCancelRequest,
|
||||
true, // Show button to enable canceling reservation.
|
||||
AppResources.ActionCancelReservation,
|
||||
true, // Show button "Cancel Reservation".
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -45,19 +45,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true; // Show button to search lock.
|
||||
IsLockitButtonVisible = true; // Show button "Search Lock"
|
||||
}
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
/// <summary> Connect to reserved bike ask whether to book bike or not and if yes open lock. </summary>
|
||||
/// <summary> Connect to reserved bike ask whether to rent bike or not and if yes open lock. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ConnectLockAndBook();
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ConnectLockAndRentBike();
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> CancelReservation()
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("User request to cancel reservation of bike {bikeId}", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
|
@ -69,13 +70,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (alertResult == false)
|
||||
{
|
||||
// User aborted cancel process
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {l_oId} in order to cancel reservation but action was canceled.", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User canceled request to cancel reservation of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
@ -85,14 +84,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User canceled reservation of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {bikeId} but cancel reservation failed.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Invalid auth. response.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
|
@ -102,7 +103,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {l_oId} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Copri server not reachable.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
|
@ -110,7 +111,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
|
@ -125,8 +126,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
@ -134,12 +133,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Connect to reserved bike ask whether to book bike or not and if yes open lock. </summary>
|
||||
/// <summary> Connect to reserved bike ask whether to rent bike or not and if yes open lock. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> ConnectLockAndBook()
|
||||
public async Task<IRequestHandler> ConnectLockAndRentBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<ReservedDisconnected>().Information("Request to search for {bike} detected.", SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User request to connect to lock of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
@ -151,6 +150,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Repeat reservation to get a new seed/ k_user value.
|
||||
await ConnectorFactory(IsConnected).Command.CalculateAuthKeys(SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Calculation of AuthKeys successfully.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
@ -160,7 +160,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected requested bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Calculation of AuthKeys failed (Copri server not reachable).");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -169,7 +169,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected requested bike {l_oId} to scan for lock. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Calculation of AuthKeys failed. {@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
|
@ -204,6 +204,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
K_u = SelectedBike.LockInfo.UserKey
|
||||
}.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(retryCount));
|
||||
Log.ForContext<ReservedDisconnected>().Information("Connected to lock of bike {bikeId} successfully. Value is {lockState}.", SelectedBike.Id, SelectedBike.LockInfo.State);
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
@ -217,6 +219,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockBluetoothNotOn,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, bluetooth is off.", SelectedBike.Id);
|
||||
}
|
||||
else if (exception is ConnectLocationPermissionMissingException)
|
||||
{
|
||||
|
@ -226,6 +229,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorNoLocationPermission,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, location permissions are missing.", SelectedBike.Id);
|
||||
}
|
||||
else if (exception is ConnectLocationOffException)
|
||||
{
|
||||
|
@ -235,6 +239,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockLocationOff,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, location services are off.", SelectedBike.Id);
|
||||
}
|
||||
else if (exception is OutOfReachException)
|
||||
{
|
||||
|
@ -244,10 +249,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, lock is out of reach.", SelectedBike.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock can not be found. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock of bike {bikeId} can not be found. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
string message;
|
||||
if (retryCount < 2)
|
||||
|
@ -284,7 +290,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
if (!(result?.State is LockitLockingState lockingState))
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock for bike {bikeId} not found.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
|
@ -300,34 +306,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = lockingState.GetLockingState();
|
||||
// get current locking state
|
||||
var state = lockingState.GetLockingState();
|
||||
SelectedBike.LockInfo.State = state;
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information($"State for bike {SelectedBike.Id} updated successfully. Value is {SelectedBike.LockInfo.State}.");
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
var alertResult
|
||||
= SelectedBike.LockInfo.State != LockingState.Open
|
||||
? await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo)
|
||||
: await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected recently requested bike {bike} in order to reserve but did deny to book bike.", SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User denied to book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock of bike {bikeId} can not be disconnected. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
@ -340,23 +352,30 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected recently requested bike {bike} in order to book.", SelectedBike);
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User request to book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
}
|
||||
Log.ForContext<ReservedDisconnected>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected recently requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Booking of bike {bikeId} failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
|
@ -365,11 +384,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Booking of bike {bikeId} failed. {@exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -382,102 +401,91 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
// Unlock bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock can not be opened. {Exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. Bold is blocked. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. Bold status is unknown. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. lock reports state closed. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock from {bikeId} can not be opened. {Exception}", SelectedBike.Id, exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedDisconnected>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Battery state of lock from {bikeId} can not be read, bike out of range. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Battery state can not be read. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Battery state of lock from {bikeId} can not be read. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
|
@ -495,37 +503,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedDisconnected>().Debug("{response}.", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
|
|
@ -12,6 +12,7 @@ using TINK.Services.BluetoothLock.Exception;
|
|||
using TINK.Services.CopriApi.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -37,8 +38,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseOrBook,
|
||||
true, // Show button to enable canceling reservation.
|
||||
AppResources.ActionCloseLock,
|
||||
true, // Show button "Close Lock"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -49,246 +50,133 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = "Alarm/ Sounds verwalten";
|
||||
IsLockitButtonVisible = activeUser.DebugLevel > 0; // Will be visible in future version of user with leveraged privileges.
|
||||
LockitButtonText = activeUser.DebugLevel.HasFlag(Model.User.Account.Permissions.ManageAlarmAndSounds) ? "Alarm/ Sounds verwalten" : AppResources.ActionRentBike;
|
||||
IsLockitButtonVisible = true; // Only users with special permissions are allowed to set alarm. Regular user gets options "Rent Bike"
|
||||
}
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockOrDoBook();
|
||||
/// <summary> Close lock. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLock();
|
||||
|
||||
/// <summary> Manage sound/ alarm settings. </summary>
|
||||
/// <summary> Manage sound/ alarm settings or Rent bike. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ManageLockSettings();
|
||||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
=> ActiveUser.DebugLevel.HasFlag(Model.User.Account.Permissions.ManageAlarmAndSounds)
|
||||
? await ManageLockSettings()
|
||||
: await RentBike();
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> CloseLockOrDoBook()
|
||||
/// <summary> Rent bike. </summary>
|
||||
public async Task<IRequestHandler> RentBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<ReservedOpen>().Information("User request to book bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCloseOrBook, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.ActionClose,
|
||||
AppResources.ActionBook);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User decided to book
|
||||
Log.ForContext<ReservedOpen>().Information("User selected requested bike {bike} in order to book.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify copri about unlock action in order to start booking.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedOpen>().Information("User selected requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// If booking failed lock bike again because bike is only reserved.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("Locking bike after booking failure failed. {Exception}", exception);
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedOpen>().Information("User booked bike {bike} successfully.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Close lock and cancel reservation.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock bike is moving. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoltBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
string.Format(AppResources.ErrorCloseLock, exception.Message),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||||
// Book bike
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
Log.ForContext<ReservedOpen>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = String.Empty;
|
||||
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedOpen>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<ReservedOpen>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedOpen>().Information("User selected reserved bike {l_oId} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedOpen>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedOpen>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedOpen>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
// get current charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<ReservedOpen>().Debug("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedOpen>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
|
@ -301,7 +189,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Close lock
|
||||
Log.ForContext<ReservedOpen>().Information("User selected disposable bike {bike} in order to manage sound/ alarm settings.", SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("User selected bike {bikeId} in order to manage sound/ alarm settings.", SelectedBike.Id);
|
||||
|
||||
// Alarm and sounds are on, toggle to off.
|
||||
// Switch off sound.
|
||||
|
@ -411,5 +299,208 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
Log.ForContext<ReservedOpen>().Information("User request to close lock of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} closed successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} can not be closed.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is moving.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoltBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed .", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Information("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedOpen>().Debug("{response}.", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Ask whether to cancel reservation
|
||||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (result == true)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("User request to cancel reservation of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("User canceled reservation of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<ReservedOpen>().Information("Canceling reservation of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedOpen>().Debug("Invalid auth. response.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Debug("Copri server not reachable. No web.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}.", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
if (SelectedBike.LockInfo.State == LockingState.Closed)
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndBook, // BT button text "Schloss öffnen und Rad mieten."
|
||||
false, // Show button to enabled returning of bike.
|
||||
AppResources.ActionOpenLockAndRentBike, // Open Lock & Rent Bike
|
||||
true, // Show button "Open Lock & Rent Bike.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -47,7 +47,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen"
|
||||
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
|
||||
_closeLockActionViewModel = new CloseLockActionViewModel<BookedOpen>(
|
||||
|
@ -57,34 +57,46 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the view model for close action.
|
||||
/// </summary>
|
||||
private CloseLockActionViewModel<BookedOpen> _closeLockActionViewModel;
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await OpenLock();
|
||||
|
||||
/// <summary> Close lock (and return bike).</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
{
|
||||
await _closeLockActionViewModel.CloseLockAsync();
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to unlock bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// open lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedUnknown>().Information("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -94,7 +106,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -104,7 +116,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. lock reports state unkwnown. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock reports that state is still unknown.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -115,7 +127,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock reports that it is closed.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -125,7 +137,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -140,35 +152,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedUnknown>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -180,36 +188,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedUnknown>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<ReservedUnknown>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedUnknown>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
@ -217,20 +225,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the view model for close action.
|
||||
/// </summary>
|
||||
private CloseLockActionViewModel<BookedOpen> _closeLockActionViewModel;
|
||||
|
||||
/// <summary> Close lock (and return bike).</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
{
|
||||
await _closeLockActionViewModel.CloseLockAsync();
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock progress.
|
||||
/// </summary>
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndPause;
|
||||
LockitButtonText = AppResources.ActionOpenLockAndPause;
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndPause; // Lock is open but show button anyway to be less prone to errors.
|
||||
LockitButtonText = AppResources.ActionOpenLockAndPause; // Lock is open but show button anyway to be less prone to errors.
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndBook, // Button text: "Schloss öffnen & Rad mieten"
|
||||
AppResources.ActionOpenLockAndRentBike, // Button text: "Schloss öffnen & Rad mieten"
|
||||
true, // Show copri button to enable booking and opening
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -39,7 +39,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionRequest; // Copri text: "Rad reservieren"
|
||||
LockitButtonText = AppResources.ActionReserveBike; // Copri text: "Rad reservieren"
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCancelRequest, // Copri button text: "Reservierung abbrechen"
|
||||
AppResources.ActionCancelReservation, // Copri button text: "Reservierung abbrechen"
|
||||
true, // Show button to enable canceling reservation.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -44,7 +44,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndBook; // Button text: "Schloss öffnen & Rad mieten"
|
||||
LockitButtonText = AppResources.ActionOpenLockAndRentBike; // Button text: "Schloss öffnen & Rad mieten"
|
||||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
}
|
||||
|
||||
|
|
|
@ -500,7 +500,7 @@ namespace TINK.ViewModel.Bikes
|
|||
/// Transforms bikes view model object to string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
var l_oToString = string.Empty;
|
||||
foreach (var item in Items)
|
||||
|
|
|
@ -167,36 +167,20 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
/// <summary> Command object to bind login page redirect link to view model.</summary>
|
||||
public System.Windows.Input.ICommand ContactSupportClickedCommand
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenSupportPageAsync());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenSupportPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Command object to bind login page redirect link to view model.</summary>
|
||||
public System.Windows.Input.ICommand LoginRequiredHintClickedCommand
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenLoginPageAsync());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Opens login page. </summary>
|
||||
#if USEFLYOUT
|
||||
public void OpenLoginPageAsync()
|
||||
#else
|
||||
public async Task OpenLoginPageAsync()
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Switch to map page
|
||||
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
@ -206,11 +190,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
}
|
||||
|
||||
/// <summary> Opens support. </summary>
|
||||
#if USEFLYOUT
|
||||
public void OpenSupportPageAsync()
|
||||
#else
|
||||
public async Task OpenSupportPageAsync()
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -222,11 +202,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
// Switch to map page
|
||||
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingFeedbackAndContact);
|
||||
#else
|
||||
await ViewService.ShowPage("//ContactPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
@ -257,7 +233,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
ActionText = AppResources.ActivityTextBikesAtStationGetBikes;
|
||||
|
||||
var bikesAll = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
|
||||
var bikesAll = await ConnectorFactory(IsConnected).Query.GetBikesAsync(Station?.OperatorUri);
|
||||
|
||||
Exception = bikesAll.Exception; // Update communication error from query for bikes at station.
|
||||
|
||||
|
|
|
@ -237,28 +237,16 @@ namespace TINK.ViewModel.Info
|
|||
|
||||
/// <summary> Command object to bind login button to view model. </summary>
|
||||
public ICommand OnSelectStationRequest
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenSelectStationPage());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenSelectStationPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Opens login page. </summary>
|
||||
#if USEFLYOUT
|
||||
public void OpenSelectStationPage()
|
||||
#else
|
||||
public async Task OpenSelectStationPageAsync()
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Switch to map page
|
||||
|
||||
#if USEFLYOUT
|
||||
ViewService.PushAsync(ViewTypes.SelectStationPage);
|
||||
#else
|
||||
await ViewService.PushAsync(ViewTypes.SelectStationPage);
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
|
|
@ -12,9 +12,6 @@ using System.Threading.Tasks;
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms.GoogleMaps;
|
||||
using System.Collections.ObjectModel;
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.Services.Permissions;
|
||||
using Xamarin.Essentials;
|
||||
using System.Threading;
|
||||
|
@ -63,10 +60,6 @@ namespace TINK.ViewModel.Contact
|
|||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigation m_oNavigation;
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigationMasterDetail;
|
||||
#endif
|
||||
private ObservableCollection<Pin> pins;
|
||||
|
||||
public ObservableCollection<Pin> Pins
|
||||
|
@ -139,21 +132,10 @@ namespace TINK.ViewModel.Contact
|
|||
m_oNavigation = navigation
|
||||
?? throw new ArgumentException("Can not instantiate map page view model- object. No navigation service available.");
|
||||
|
||||
#if USEFLYOUT
|
||||
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
|
||||
#endif
|
||||
|
||||
IsConnected = TinkApp.GetIsConnected();
|
||||
}
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail
|
||||
{
|
||||
set { m_oNavigationMasterDetail = value; }
|
||||
}
|
||||
#endif
|
||||
|
||||
public Command<PinClickedEventArgs> PinClickedCommand => new Command<PinClickedEventArgs>(
|
||||
args =>
|
||||
{
|
||||
|
@ -461,19 +443,9 @@ namespace TINK.ViewModel.Contact
|
|||
IsMapPageEnabled = false;
|
||||
|
||||
TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId)
|
||||
?? new Model.Stations.StationNS.Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
?? new Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
m_oNavigation.ShowPage(
|
||||
typeof(BikesAtStationPage),
|
||||
p_strStationName);
|
||||
#else
|
||||
#if USEFLYOUT
|
||||
// Show page.
|
||||
ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingContactPageTitle);
|
||||
#else
|
||||
await ViewService.ShowPage("//ContactPage");
|
||||
#endif
|
||||
IsMapPageEnabled = true;
|
||||
ActionText = string.Empty;
|
||||
}
|
||||
|
@ -489,7 +461,6 @@ namespace TINK.ViewModel.Contact
|
|||
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TINK.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.Info.BikeInfo
|
||||
{
|
||||
public class BikeInfoViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference on view service to show modal notifications and to perform navigation.
|
||||
/// </summary>
|
||||
private readonly IViewService m_oViewService;
|
||||
|
||||
|
||||
/// <param name="p_oViewService">Interface to actuate methods on GUI.</param>
|
||||
public BikeInfoViewModel(Func<string, ImageSource> imageSourceFunc, IViewService p_oViewService)
|
||||
{
|
||||
m_oViewService = p_oViewService
|
||||
?? throw new ArgumentException("Can not instantiate bike info page view model- object. No view available.");
|
||||
|
||||
CarouselItems = new List<CourouselPageItemViewModel>();
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Anleitung Benutzung Lastenräder",
|
||||
$"Diese Anleitung wird einmalig nach der Anmeldung angezeigt.\r\nZum Blättern auf die nächste Seite bitte nach links wischen.\r\nNach Anzeige aller Seiten kann die Anleitung geschlossen werden.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction()));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Zweirädriges TINK Rad: Hochklappen des Fahrradständers (1/3)",
|
||||
"So abgestellt hat das zweirädrige Transportrad einen sicheren Stand.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_stand1_image.4HJ5PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Zweirädriges TINK Rad: Hochklappen des Fahrradständers (2/3)",
|
||||
"Zum Weiterfahren das Transportrad nach vorne bewegen, bis kein Gewicht mehr auf dem Fahrradständer liegt.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_stand2_image.RIX2PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Zweirädriges TINK Rad: Hochklappen des Fahrradständers (3/3).",
|
||||
"Den Fahrradständer mit dem Fuß nach oben drücken, bis er hörbar am Magneten (Pfeil) einrastet. So fällt er unterwegs nicht herunter.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_stand3_image.FDR7PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Dreirädriges TINK Rad: Lösen und Aktivieren der Feststellbremse (1/3).",
|
||||
"Die Feststellbremse ist der graue Stift, der aus der Bremse herausragt. Ist sie aktiv, kann das Dreirad nicht wegrollen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_brake1_image.HZ17PY_678_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Dreirädriges TINK Rad: Lösen und Aktivieren der Feststellbremse (2/3).",
|
||||
"Lösen der Feststellbremse: Die Bremse vollständig anziehen, bis der Stift wieder auf seine ursprüngliche Position herausspringt.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_brake2_image.1YBAQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Dreirädriges TINK Rad: Lösen und Aktivieren der Feststellbremse (3/3).",
|
||||
"Aktivieren der Feststellbremse: Die Bremse vollständig anziehen und den Stift hineindrücken.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_brake3_image.FJM2PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Höhenregulierung des Sattels (1/3).",
|
||||
"Hier im Bild ist der Hebel zum Einstellen des Sattels zu sehen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("seat1_image.ZQ65PY_680_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Höhenregulierung des Sattels (2/3).",
|
||||
"Durch Drücken des Hebels ist die Sattelhöhe frei verstellbar. Vergleichbar mit einem Bürostuhl bewegt sich der Sattel automatisch nach oben.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("seat2_image.QQZCQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Höhenregulierung des Sattels (3/3).",
|
||||
"Durch kräftiges Herunterdrücken des Sattels (und gleichzeitigem Betätigen des Hebels) kann der Sattel nach unten verstellt werden. Tipp: Eventuell draufsetzen und dann den Hebel betätigen, um den Sattel nach unten zu drücken.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("seat3_image.NQ5FQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Verbinden des Kindergurts (1/3).",
|
||||
"Der Gurt besteht aus drei Einzelteilen. Zunächst die oberen beiden Einzelstücke nehmen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("belt1_image.4XWCQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Verbinden des Kindergurts (2/3).",
|
||||
"Die beiden Einzelstücke zusammenfügen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("belt2_image.X3F1PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Verbinden des Kindergurts (3/3).",
|
||||
"Das obere und untere Teilstück verbinden (bis zum Einrasten). Lösen der Teilstücke durch Drücken auf den roten Knopf.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("belt3_image.DYOXPY_679_382.png")));
|
||||
}
|
||||
|
||||
/// <summary> Gets the carousel page items</summary>
|
||||
public IList<CourouselPageItemViewModel> CarouselItems { get; }
|
||||
|
||||
/// <summary> Command object to bind close button to view model. </summary>
|
||||
#if USEFLYOUT
|
||||
private Action CloseAction
|
||||
=> () => m_oViewService.ShowPage(ViewTypes.MapPage);
|
||||
#else
|
||||
private Action CloseAction
|
||||
=> async () => await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
using System;
|
||||
using System.Windows.Input;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.Info.BikeInfo
|
||||
{
|
||||
public class CourouselPageItemViewModel
|
||||
{
|
||||
public CourouselPageItemViewModel(
|
||||
string p_strTitle,
|
||||
string p_strLegend,
|
||||
int p_iCurrentPageIndex,
|
||||
Func<int> p_iPagesCountProvider,
|
||||
Action p_oCloseAction,
|
||||
ImageSource image = null)
|
||||
{
|
||||
Title = p_strTitle;
|
||||
IsImageVisble = image != null;
|
||||
if (IsImageVisble)
|
||||
{
|
||||
Image = image;
|
||||
}
|
||||
|
||||
DescriptionText = p_strLegend;
|
||||
CurrentPageIndex = p_iCurrentPageIndex;
|
||||
PagesCountProvider = p_iPagesCountProvider;
|
||||
CloseAction = p_oCloseAction;
|
||||
}
|
||||
|
||||
/// <summary> Gets the title of the navigation page. </summary>
|
||||
public string Title { get; }
|
||||
|
||||
public bool IsImageVisble { get; }
|
||||
|
||||
/// <summary> Gets the image. </summary>
|
||||
public ImageSource Image { get; }
|
||||
|
||||
/// <summary> Gets the text which describes the image. </summary>
|
||||
public string DescriptionText { get; }
|
||||
|
||||
/// <summary> Get the progress of the carousselling progress.</summary>
|
||||
public double ProgressValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var l_oCount = PagesCountProvider();
|
||||
|
||||
return l_oCount > 0 ? (double)CurrentPageIndex / l_oCount : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets if user can leave carousel page.</summary>
|
||||
public bool IsCloseVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return PagesCountProvider() == CurrentPageIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Command object to bind close button to view model. </summary>
|
||||
public ICommand OnCloseRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Command(() => CloseAction());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns one based index of the current page. </summary>
|
||||
private int CurrentPageIndex { get; }
|
||||
|
||||
/// <summary> Gets the count of carousel pages. </summary>
|
||||
private Func<int> PagesCountProvider { get; }
|
||||
|
||||
/// <summary> Action to actuate when close is invoked. </summary>
|
||||
private Action CloseAction { get; }
|
||||
}
|
||||
}
|
|
@ -24,10 +24,6 @@ namespace TINK.ViewModel
|
|||
/// </summary>
|
||||
private readonly IViewService m_oViewService;
|
||||
|
||||
#if BACKSTYLE
|
||||
/// <summary> Reference to navigation object to navigate back to map page when login succeeded. </summary>
|
||||
private INavigation m_oNavigation;
|
||||
#endif
|
||||
/// <summary> Reference on the tink app instance. </summary>
|
||||
private ITinkApp TinkApp { get; }
|
||||
|
||||
|
@ -103,12 +99,7 @@ namespace TINK.ViewModel
|
|||
public LoginPageViewModel(
|
||||
ITinkApp tinkApp,
|
||||
Action<string> openUrlInExternalBrowser,
|
||||
#if !BACKSTYLE
|
||||
IViewService p_oViewService)
|
||||
#else
|
||||
IViewService p_oViewService,
|
||||
INavigation p_oNavigation)
|
||||
#endif
|
||||
{
|
||||
TinkApp = tinkApp
|
||||
?? throw new ArgumentException("Can not instantiate map page view model- object. No tink app object available.");
|
||||
|
@ -118,10 +109,6 @@ namespace TINK.ViewModel
|
|||
|
||||
m_oViewService = p_oViewService
|
||||
?? throw new ArgumentException("Can not instantiate login page view model- object. No view available.");
|
||||
#if BACKSTYLE
|
||||
m_oNavigation = p_oNavigation
|
||||
?? throw new ArgumentException("Can not instantiate login page view model- object. No navigation service available.");
|
||||
#endif
|
||||
|
||||
m_strMailAddress = tinkApp.ActiveUser.Mail;
|
||||
m_strPassword = tinkApp.ActiveUser.Password;
|
||||
|
@ -250,11 +237,7 @@ namespace TINK.ViewModel
|
|||
/// <summary>
|
||||
/// User request to log in.
|
||||
/// </summary>
|
||||
#if BACKSTYLE
|
||||
public async void Login()
|
||||
#else
|
||||
public async Task Login()
|
||||
#endif
|
||||
{
|
||||
if (!IsLoginRequestAllowed)
|
||||
{
|
||||
|
@ -398,23 +381,14 @@ namespace TINK.ViewModel
|
|||
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.CARGOBIKE))
|
||||
{
|
||||
// No need to show "Anleitung TINK Räder" because user can not use tink.
|
||||
#if USEFLYOUT
|
||||
|
||||
m_oViewService.ShowPage(account.IsAgbAcknowledged ? ViewTypes.MapPage : ViewTypes.ManageAccountPage);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch to map page
|
||||
#if USEFLYOUT
|
||||
m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
@ -425,10 +399,6 @@ namespace TINK.ViewModel
|
|||
return;
|
||||
}
|
||||
|
||||
#if BACKSTYLE
|
||||
// Navigate back to map page.
|
||||
await m_oNavigation.PopToRootAsync();
|
||||
#endif
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
}
|
||||
|
|
|
@ -12,9 +12,6 @@ using System.Threading.Tasks;
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms.GoogleMaps;
|
||||
using System.Collections.ObjectModel;
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.Settings;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
|
@ -28,9 +25,6 @@ using TINK.Model.State;
|
|||
using TINK.Model.Bikes.BikeInfoNS.BC;
|
||||
using TINK.Model.Stations.StationNS;
|
||||
|
||||
#if !TRYNOTBACKSTYLE
|
||||
#endif
|
||||
|
||||
namespace TINK.ViewModel.Map
|
||||
{
|
||||
public class MapPageViewModel : INotifyPropertyChanged
|
||||
|
@ -75,10 +69,6 @@ namespace TINK.ViewModel.Map
|
|||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigation m_oNavigation;
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigationMasterDetail;
|
||||
#endif
|
||||
private ObservableCollection<Pin> pins;
|
||||
|
||||
public ObservableCollection<Pin> Pins
|
||||
|
@ -153,9 +143,6 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
m_oViewUpdateManager = new IdlePollingUpdateTaskManager();
|
||||
|
||||
#if USEFLYOUT
|
||||
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
|
||||
#endif
|
||||
|
||||
Polling = PollingParameters.NoPolling;
|
||||
|
||||
|
@ -180,13 +167,6 @@ namespace TINK.ViewModel.Map
|
|||
}
|
||||
}
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail
|
||||
{
|
||||
set { m_oNavigationMasterDetail = value; }
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Counts the number of reserved or occupied bikes -> visualized in MyBikes-Icon
|
||||
/// </summary>
|
||||
|
@ -742,13 +722,8 @@ namespace TINK.ViewModel.Map
|
|||
IsMapPageEnabled = false;
|
||||
|
||||
TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId)
|
||||
?? new Model.Stations.StationNS.Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
?? new Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
m_oNavigation.ShowPage(
|
||||
typeof(BikesAtStationPage),
|
||||
p_strStationName);
|
||||
#else
|
||||
{
|
||||
// Show page.
|
||||
await ViewService.PushAsync(ViewTypes.BikesAtStation);
|
||||
|
@ -768,7 +743,6 @@ namespace TINK.ViewModel.Map
|
|||
$"{AppResources.ErrorPageNotLoaded}\r\n {exception.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -168,10 +168,7 @@ namespace TINK.ViewModel
|
|||
{ typeof(Services.BluetoothLock.BLE.LockItByScanServiceEventBased).FullName, "Live - Scan" },
|
||||
{ typeof(Services.BluetoothLock.BLE.LockItByScanServicePolling).FullName, "Live - Scan (Polling)" },
|
||||
{ typeof(Services.BluetoothLock.BLE.LockItByGuidService).FullName, "Live - Guid" },
|
||||
/* { typeof(Services.BluetoothLock.Arendi.LockItByGuidService).FullName, "Live - Guid (Arendi)" },
|
||||
{ typeof(Services.BluetoothLock.Arendi.LockItByScanService).FullName, "Live - Scan (Arendi)" },
|
||||
{ typeof(Services.BluetoothLock.Bluetoothle.LockItByGuidService).FullName, "Live - Guid (Ritchie)" }, */
|
||||
},
|
||||
},
|
||||
TinkApp.LocksServices.Active.GetType().FullName));
|
||||
|
||||
GeolocationServices = new ServicesViewModel(
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace TINK
|
|||
MapPage,
|
||||
RegisterPage,
|
||||
PasswordForgottenPage,
|
||||
BikeInfoCarouselPage,
|
||||
MyBikesPage,
|
||||
SettingsPage,
|
||||
TabbedPageInfo,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue