mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-04-22 21:06:30 +02:00
Version 3.0.370
This commit is contained in:
parent
f5cf9bb22f
commit
bdb2dec1c1
233 changed files with 10252 additions and 6779 deletions
|
@ -3,7 +3,11 @@ using System.Collections.Generic;
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Serilog;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Stations.StationNS;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.ViewModel;
|
||||
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable;
|
||||
|
||||
|
@ -12,10 +16,49 @@ namespace TINK.Model.Bikes
|
|||
/// <summary> Holds entity of bikes. </summary>
|
||||
public class BikeCollectionMutable : ObservableCollection<BikeInfoMutable>, IBikeDictionaryMutable<BikeInfoMutable>
|
||||
{
|
||||
/// <summary> Provides a connector object.</summary>
|
||||
private Func<bool, IConnector> ConnectorFactory { get; }
|
||||
|
||||
/// <summary> Provides geolocation information.</summary>
|
||||
private IGeolocationService GeolocationService { get; }
|
||||
|
||||
/// <summary> Provides a connector object.</summary>
|
||||
private ILocksService LockService { get; }
|
||||
|
||||
/// <summary> Delegate to retrieve connected state. </summary>
|
||||
private Func<bool> IsConnectedDelegate { get; }
|
||||
|
||||
/// <summary>Object to manage update of view model objects from Copri.</summary>
|
||||
protected Func<IPollingUpdateTaskManager> ViewUpdateManager { get; }
|
||||
|
||||
/// <summary> Constructs a mutable bike collection object. </summary>
|
||||
public BikeCollectionMutable()
|
||||
/// <param name="geolocation">Provides geolocation information.</param>
|
||||
/// <param name="lockService">Manages lock.</param>
|
||||
/// <param name="isConnectedDelegate">Delegate to retrieve connected state.</param>
|
||||
/// <param name="connectorFactory">Provides a connector object.</param>
|
||||
public BikeCollectionMutable(
|
||||
IGeolocationService geolocation,
|
||||
ILocksService lockService,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager)
|
||||
{
|
||||
SelectedBike = null;
|
||||
|
||||
GeolocationService = geolocation
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeCollectionMutable)}- object. No geolocation object available.");
|
||||
|
||||
LockService = lockService
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeCollectionMutable)}- object. No lock service object available.");
|
||||
|
||||
IsConnectedDelegate = isConnectedDelegate
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeCollectionMutable)}- object. No is connected delegate available.");
|
||||
|
||||
ConnectorFactory = connectorFactory
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeCollectionMutable)}- object. No connector available.");
|
||||
|
||||
ViewUpdateManager = viewUpdateManager
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeCollectionMutable)}- object. No view update manager available.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -44,7 +87,14 @@ namespace TINK.Model.Bikes
|
|||
// Check if bike has to be added to list of existing station.
|
||||
if (ContainsKey(bikeInfo.Id) == false)
|
||||
{
|
||||
var bikeInfoMutable = BikeInfoMutableFactory.Create(bikeInfo, stationName);
|
||||
var bikeInfoMutable = BikeInfoMutableFactory.Create(
|
||||
GeolocationService,
|
||||
LockService,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
ViewUpdateManager,
|
||||
bikeInfo,
|
||||
stationName);
|
||||
if (bikeInfoMutable != null)
|
||||
{
|
||||
// Bike does not yet exist in list of bikes.
|
||||
|
@ -143,13 +193,26 @@ namespace TINK.Model.Bikes
|
|||
/// </summary>
|
||||
public static class BikeInfoMutableFactory
|
||||
{
|
||||
/// <param name="viewUpdateManager">Manages update of view model objects from Copri.</param>
|
||||
public static BikeInfoMutable Create(
|
||||
IGeolocationService geolocation,
|
||||
ILocksService lockService,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
BikeInfo bikeInfo,
|
||||
string stationName)
|
||||
{
|
||||
if (bikeInfo is BikeInfoNS.BluetoothLock.BikeInfo btBikeInfo)
|
||||
{
|
||||
return new BikeInfoNS.BluetoothLock.BikeInfoMutable(btBikeInfo, stationName);
|
||||
return new BikeInfoNS.BluetoothLock.BikeInfoMutable(
|
||||
geolocation,
|
||||
lockService,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
viewUpdateManager,
|
||||
btBikeInfo,
|
||||
stationName);
|
||||
}
|
||||
else if (bikeInfo is BikeInfoNS.CopriLock.BikeInfo copriBikeInfo)
|
||||
{
|
||||
|
|
|
@ -1,11 +1,42 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.ViewModel;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand;
|
||||
|
||||
namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
|
||||
{
|
||||
public class BikeInfoMutable : BC.BikeInfoMutable, IBikeInfoMutable
|
||||
{
|
||||
/// <summary> Provides a connector object.</summary>
|
||||
private Func<bool, IConnector> ConnectorFactory { get; }
|
||||
|
||||
/// <summary> Provides geolocation information.</summary>
|
||||
private IGeolocationService GeolocationService { get; }
|
||||
|
||||
/// <summary> Provides a connector object.</summary>
|
||||
private ILocksService LockService { get; }
|
||||
|
||||
/// <summary> Delegate to retrieve connected state. </summary>
|
||||
private Func<bool> IsConnectedDelegate { get; }
|
||||
|
||||
/// <summary>Object to manage update of view model objects from Copri.</summary>
|
||||
protected Func<IPollingUpdateTaskManager> ViewUpdateManager { get; }
|
||||
|
||||
/// <summary> Constructs a bike object from source. </summary>
|
||||
public BikeInfoMutable(BikeInfo bike, string stationName) : base(
|
||||
/// <param name="viewUpdateManager">Manages update of view model objects from Copri.</param>
|
||||
public BikeInfoMutable(
|
||||
IGeolocationService geolocation,
|
||||
ILocksService lockService,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
BikeInfo bike,
|
||||
string stationName) : base(
|
||||
bike != null
|
||||
? bike.Bike
|
||||
: throw new ArgumentNullException(nameof(bike)),
|
||||
|
@ -27,12 +58,58 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
|
|||
bike.LockInfo.AdminKey,
|
||||
bike.LockInfo.Seed,
|
||||
bike.LockInfo.State);
|
||||
|
||||
GeolocationService = geolocation
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeInfoMutable)}- object. No geolocation object available.");
|
||||
|
||||
LockService = lockService
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeInfoMutable)}- object. No lock service object available.");
|
||||
|
||||
IsConnectedDelegate = isConnectedDelegate
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeInfoMutable)}- object. No is connected delegate available.");
|
||||
|
||||
ConnectorFactory = connectorFactory
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeInfoMutable)}- object. No connector available.");
|
||||
|
||||
ViewUpdateManager = viewUpdateManager
|
||||
?? throw new ArgumentException($"Can not instantiate {nameof(BikeInfoMutable)}- object. No update manger available.");
|
||||
}
|
||||
|
||||
public LockInfoMutable LockInfo { get; }
|
||||
|
||||
ILockInfoMutable IBikeInfoMutable.LockInfo => LockInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Closes the lock and updates copri.
|
||||
/// </summary>
|
||||
/// <param name="listener">View model to process closing notifications.</param>
|
||||
/// <param name="stopPollingTask">Task which stops polling.</param>
|
||||
public async Task CloseLockAsync(
|
||||
ICloseCommandListener listener,
|
||||
Task stopPollingTask)
|
||||
{
|
||||
await InvokeAsync<BikeInfoMutable>(
|
||||
this,
|
||||
GeolocationService,
|
||||
LockService,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
listener,
|
||||
stopPollingTask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location of the locked bike.
|
||||
/// </summary>
|
||||
/// <param name="listener">View model to process notifications.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<LocationDto> GetLockedBikeLocationAsync(IGetLockedLocationCommandListener listener) =>
|
||||
await InvokeAsync<BikeInfoMutable>(
|
||||
this,
|
||||
GeolocationService,
|
||||
LockService,
|
||||
listener: listener);
|
||||
|
||||
public new string ToString()
|
||||
{
|
||||
return $"Id={Id}{(TypeOfBike != null ? $";type={TypeOfBike}" : "")};state={State.ToString()}";
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Geolocation;
|
||||
|
||||
namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
||||
{
|
||||
public static class CloseCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible steps of closing a lock.
|
||||
/// </summary>
|
||||
public enum Step
|
||||
{
|
||||
StartStopingPolling,
|
||||
StartingQueryingLocation,
|
||||
ClosingLock,
|
||||
WaitStopPollingQueryLocation,
|
||||
/// <summary>
|
||||
/// Notifies back end about modified lock state.
|
||||
/// </summary>
|
||||
UpdateLockingState,
|
||||
QueryLocationTerminated
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Possible steps of closing a lock.
|
||||
/// </summary>
|
||||
public enum State
|
||||
{
|
||||
OutOfReachError,
|
||||
CouldntCloseMovingError,
|
||||
CouldntCloseBoltBlockedError,
|
||||
GeneralCloseError,
|
||||
StartGeolocationException,
|
||||
WaitGeolocationException,
|
||||
WebConnectFailed,
|
||||
ResponseIsInvalid,
|
||||
BackendUpdateFailed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface to notify view model about steps/ state changes of closing process.
|
||||
/// </summary>
|
||||
public interface ICloseCommandListener
|
||||
{
|
||||
/// <summary>
|
||||
/// Reports current step.
|
||||
/// </summary>
|
||||
/// <param name="currentStep">Current step to report.</param>
|
||||
void ReportStep(Step currentStep);
|
||||
|
||||
/// <summary>
|
||||
/// Reports current state.
|
||||
/// </summary>
|
||||
/// <param name="currentState">Current state to report.</param>
|
||||
/// <param name="message">Message describing the current state.</param>
|
||||
/// <returns></returns>
|
||||
Task ReportStateAsync(State currentState, string message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the lock and updates copri.
|
||||
/// </summary>
|
||||
/// <param name="listener">Interface to notify view model about steps/ state changes of closing process.</param>
|
||||
/// <param name="stopPolling">Task which stops polling.</param>
|
||||
public static async Task InvokeAsync<T>(
|
||||
IBikeInfoMutable bike,
|
||||
IGeolocationService geolocation,
|
||||
ILocksService lockService,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactor,
|
||||
ICloseCommandListener listener,
|
||||
Task stopPollingTask)
|
||||
{
|
||||
// Invokes member to notify about step being started.
|
||||
void InvokeCurrentStep(Step step)
|
||||
{
|
||||
if (listener == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
listener.ReportStep(step);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking step- action for set {step} ", ex, step);
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes member to notify about state change.
|
||||
async Task InvokeCurrentStateAsync(State state, string message)
|
||||
{
|
||||
if (listener == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await listener.ReportStateAsync(state, message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking state- action for set {state} ", ex, state);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for geolocation and polling task to stop (on finished or on canceled).
|
||||
async Task<IGeolocation> WaitForPendingTasks(Task<IGeolocation> locationTask)
|
||||
{
|
||||
// Step: Wait until getting geolocation has completed.
|
||||
InvokeCurrentStep(Step.WaitStopPollingQueryLocation);
|
||||
Log.ForContext<T>().Debug($"Waiting on steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} to finish...");
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(new List<Task> { locationTask, stopPollingTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 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);
|
||||
InvokeCurrentStep(Step.QueryLocationTerminated);
|
||||
return null;
|
||||
}
|
||||
|
||||
Log.ForContext<T>().Debug($"Steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} finished.");
|
||||
InvokeCurrentStep(Step.QueryLocationTerminated);
|
||||
return locationTask.Result;
|
||||
}
|
||||
|
||||
// Updates locking state
|
||||
async Task UpdateLockingState(IGeolocation location, DateTime timeStamp)
|
||||
{
|
||||
// Step: Update backend.
|
||||
InvokeCurrentStep(Step.UpdateLockingState);
|
||||
try
|
||||
{
|
||||
await connectorFactor(true).Command.UpdateLockingStateAsync(
|
||||
bike,
|
||||
location != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = location.Latitude,
|
||||
Longitude = location.Longitude,
|
||||
Accuracy = location.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(location.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
//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);
|
||||
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);
|
||||
await InvokeCurrentStateAsync(State.ResponseIsInvalid, exception.Message);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Error("User locked bike {bike} in order to pause ride but updating failed. {@l_oException}", bike.Id, exception);
|
||||
await InvokeCurrentStateAsync(State.BackendUpdateFailed, exception.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start query geolocation data.
|
||||
Log.ForContext<T>().Debug($"Starting step {Step.StartingQueryingLocation}...");
|
||||
InvokeCurrentStep(Step.StartingQueryingLocation);
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<IGeolocation> currentLocationTask = Task.FromResult<IGeolocation>(null);
|
||||
var timeStampNow = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = geolocation.GetAsync(ctsLocation.Token, timeStampNow);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<T>().Information("Starting query location failed. {Exception}", bike, ex);
|
||||
await InvokeCurrentStateAsync(State.StartGeolocationException, ex.Message);
|
||||
}
|
||||
|
||||
// Close lock.
|
||||
IGeolocation currentLocation;
|
||||
Log.ForContext<T>().Debug($"Starting step {Step.ClosingLock}...");
|
||||
InvokeCurrentStep(Step.ClosingLock);
|
||||
LockitLockingState? lockingState;
|
||||
try
|
||||
{
|
||||
lockingState = await lockService[bike.LockInfo.Id].CloseAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<T>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
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);
|
||||
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);
|
||||
await InvokeCurrentStateAsync(State.CouldntCloseBoltBlockedError, exception.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Debug("Lock can not be closed. Lock is out of reach. {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();
|
||||
|
||||
//// Step: Wait until getting geolocation and stop polling has completed.
|
||||
currentLocation = await WaitForPendingTasks(currentLocationTask);
|
||||
|
||||
// Update current state from exception
|
||||
bike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
if (!isConnectedDelegate())
|
||||
{
|
||||
// Lock state can not be updated because there is no connected.
|
||||
throw;
|
||||
}
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
// Locking state can not be updated because lock is not connected.
|
||||
throw;
|
||||
}
|
||||
|
||||
// Step: Update backend.
|
||||
// Do this even if current lock state is open (lock state must not necessarily be open before try to open, i.e. something undefined between open and closed).
|
||||
await UpdateLockingState(currentLocation, timeStampNow);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
//// Step: Wait until getting geolocation and stop polling has completed.
|
||||
currentLocation = await WaitForPendingTasks(currentLocationTask);
|
||||
|
||||
bike.LockInfo.State = lockingState?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
|
||||
// Keep geolocation where closing action occurred.
|
||||
bike.LockInfo.Location = currentLocation;
|
||||
|
||||
if (!isConnectedDelegate())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//// Step: Update backend.
|
||||
await UpdateLockingState(currentLocation, timeStampNow);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
|
||||
namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to get the locked bike location.
|
||||
/// </summary>
|
||||
public static class GetLockedLocationCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible steps of closing a lock.
|
||||
/// </summary>
|
||||
public enum Step
|
||||
{
|
||||
StartingQueryLocation,
|
||||
DisconnectingLockOnDisconnectedNoLocationError,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Possible steps of closing a lock.
|
||||
/// </summary>
|
||||
public enum State
|
||||
{
|
||||
DisconnetedNoLocationError,
|
||||
DisconnectError,
|
||||
QueryLocationSucceeded,
|
||||
QueryLocationFailed,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface to notify view model about steps/ state changes of closing process.
|
||||
/// </summary>
|
||||
public interface IGetLockedLocationCommandListener
|
||||
{
|
||||
/// <summary>
|
||||
/// Reports current step.
|
||||
/// </summary>
|
||||
/// <param name="currentStep">Current step to report.</param>
|
||||
void ReportStep(Step currentStep);
|
||||
|
||||
/// <summary>
|
||||
/// Reports current state.
|
||||
/// </summary>
|
||||
/// <param name="currentState">Current state to report.</param>
|
||||
/// <param name="message">Message describing the current state.</param>
|
||||
/// <returns></returns>
|
||||
Task ReportStateAsync(State currentState, string message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get current location.
|
||||
/// </summary>
|
||||
/// <param name="listener"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async Task<LocationDto> InvokeAsync<T>(
|
||||
IBikeInfoMutable bike,
|
||||
IGeolocationService geolocation,
|
||||
ILocksService lockService,
|
||||
Func<DateTime> dateTimeProvider = null,
|
||||
IGetLockedLocationCommandListener listener = null)
|
||||
{
|
||||
// Invokes member to notify about step being started.
|
||||
void InvokeCurrentStep(Step step)
|
||||
{
|
||||
if (listener == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
listener.ReportStep(step);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking step- action for set {step} ", ex, step);
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes member to notify about state change.
|
||||
async Task InvokeCurrentStateAsync(State state, string message)
|
||||
{
|
||||
if (listener == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await listener.ReportStateAsync(state, message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ForContext<T>().Error("An exception {exception} was thrown invoking state- action for set {state} ", ex, state);
|
||||
}
|
||||
}
|
||||
|
||||
InvokeCurrentStep(Step.StartingQueryLocation);
|
||||
|
||||
// Get geolocation which was requested when closing lock.
|
||||
IGeolocation closingLockLocation = bike.LockInfo.Location;
|
||||
|
||||
if (closingLockLocation != null)
|
||||
{
|
||||
// Location was available when closing bike. No further actions required.
|
||||
return new LocationDto.Builder
|
||||
{
|
||||
Latitude = closingLockLocation.Latitude,
|
||||
Longitude = closingLockLocation.Longitude,
|
||||
Accuracy = closingLockLocation.Accuracy ?? double.NaN,
|
||||
Age = bike.LockInfo.LastLockingStateChange is DateTime lastLockState1 ? lastLockState1.Subtract(closingLockLocation.Timestamp.DateTime) : TimeSpan.MaxValue,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// Check if bike is around => geolocation information can be queried
|
||||
var deviceState = lockService[bike.LockInfo.Id].GetDeviceState();
|
||||
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);
|
||||
|
||||
await InvokeCurrentStateAsync(State.DisconnetedNoLocationError, "");
|
||||
|
||||
// Disconnect lock.
|
||||
InvokeCurrentStep(Step.DisconnectingLockOnDisconnectedNoLocationError);
|
||||
try
|
||||
{
|
||||
bike.LockInfo.State = await lockService.DisconnectAsync(bike.LockInfo.Id, bike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
await InvokeCurrentStateAsync(State.DisconnectError, exception.Message);
|
||||
}
|
||||
|
||||
await InvokeCurrentStateAsync(State.QueryLocationSucceeded, "");
|
||||
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
// Query geolocation.
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
closingLockLocation = await geolocation.GetAsync(ctsLocation.Token, DateTime.Now);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 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);
|
||||
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
|
||||
{
|
||||
Latitude = closingLockLocation.Latitude,
|
||||
Longitude = closingLockLocation.Longitude,
|
||||
Accuracy = closingLockLocation.Accuracy ?? double.NaN,
|
||||
Age = (dateTimeProvider != null ? dateTimeProvider() : DateTime.Now).Subtract(closingLockLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null;
|
||||
|
||||
return currentLocationDto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,27 @@
|
|||
using System.Threading.Tasks;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.ViewModel.Bikes.Bike.BluetoothLock;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand;
|
||||
|
||||
namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
|
||||
{
|
||||
public interface IBikeInfoMutable : BC.IBikeInfoMutable
|
||||
{
|
||||
ILockInfoMutable LockInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Closes the lock.
|
||||
/// </summary>
|
||||
/// <param name="listener">View model to process closing notifications.</param>
|
||||
/// <param name="stopPollingTask">Task which stops polling to be awaited before updating bike object and copri communication.</param>
|
||||
Task CloseLockAsync(ICloseCommandListener listener, Task stopPollingTask);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location of the locked bike.
|
||||
/// </summary>
|
||||
/// <param name="listener">View model to process notifications.</param>
|
||||
/// <returns></returns>
|
||||
Task<LocationDto> GetLockedBikeLocationAsync(IGetLockedLocationCommandListener listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets the timestamp of the last locking state change.</summary>
|
||||
/// <summary> Gets the time stamp of the last locking state change.</summary>
|
||||
public DateTime? LastLockingStateChange { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -13,5 +13,8 @@ namespace TINK.Model
|
|||
/// <summary> Holds info about co2 saving accomplished by using cargo bike. </summary>
|
||||
public string Co2Saving { get; set; }
|
||||
|
||||
/// <summary> Holds info about accruing rental costs. </summary>
|
||||
public string RentalCosts { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,17 +203,7 @@ namespace TINK.Model.Connector
|
|||
throw new ArgumentNullException($"Can not update locking state of bike. Unexpected booking state {bike.State} detected.");
|
||||
}
|
||||
|
||||
lock_state? state = null;
|
||||
switch (bike.LockInfo.State)
|
||||
{
|
||||
case LockingState.Open:
|
||||
state = lock_state.unlocked;
|
||||
break;
|
||||
|
||||
case LockingState.Closed:
|
||||
state = lock_state.locked;
|
||||
break;
|
||||
}
|
||||
lock_state? state = RequestBuilderHelper.GetLockState(bike.LockInfo.State);
|
||||
|
||||
if (!state.HasValue)
|
||||
{
|
||||
|
|
|
@ -392,6 +392,7 @@ namespace TINK.Model.Connector
|
|||
public static bool GetIsFeedbackPending(this BikeInfoAvailable bike)
|
||||
=> bike.co2saving != null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of bikes available at station.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using TINK.Repository.Response;
|
||||
|
@ -13,7 +13,8 @@ namespace TINK.Model.Connector.Updater
|
|||
{
|
||||
var bookingFinished = new BookingFinishedModel
|
||||
{
|
||||
Co2Saving = response?.co2saving
|
||||
Co2Saving = response?.co2saving,
|
||||
RentalCosts = "0 €",
|
||||
};
|
||||
|
||||
if (response?.user_miniquery == null)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using TINK.Model.MiniSurvey;
|
||||
using TINK.Model.MiniSurvey;
|
||||
|
||||
namespace TINK.Model
|
||||
{
|
||||
|
@ -9,5 +9,8 @@ namespace TINK.Model
|
|||
|
||||
/// <summary> Holds info about co2 saving accomplished by using cargo bike. </summary>
|
||||
string Co2Saving { get; set; }
|
||||
|
||||
/// <summary> Holds info about accruing rental costs. </summary>
|
||||
string RentalCosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
12
TINKLib/Model/Message/IMessage.cs
Normal file
12
TINKLib/Model/Message/IMessage.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TINK.Model.Message
|
||||
{
|
||||
public interface IMessage
|
||||
{
|
||||
void LongAlert(string message);
|
||||
void ShortAlert(string message);
|
||||
}
|
||||
}
|
67
TINKLib/Model/Message/Message.cs
Normal file
67
TINKLib/Model/Message/Message.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Android.App;
|
||||
using Android.Widget;
|
||||
using Foundation;
|
||||
using TINK.Model.Message;
|
||||
using UIKit;
|
||||
|
||||
[assembly: Xamarin.Forms.Dependency(typeof(MessageAndroid))]
|
||||
[assembly: Xamarin.Forms.Dependency(typeof(MessageIOS))]
|
||||
namespace TINK.Model.Message
|
||||
{
|
||||
public class MessageAndroid : IMessage
|
||||
{
|
||||
public void LongAlert(string message)
|
||||
{
|
||||
Toast.MakeText(Application.Context, message, ToastLength.Long).Show();
|
||||
}
|
||||
|
||||
public void ShortAlert(string message)
|
||||
{
|
||||
Toast.MakeText(Application.Context, message, ToastLength.Short).Show();
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageIOS : IMessage
|
||||
{
|
||||
const double LONG_DELAY = 3.5;
|
||||
const double SHORT_DELAY = 0.75;
|
||||
|
||||
public void LongAlert(string message)
|
||||
{
|
||||
ShowAlert(message, LONG_DELAY);
|
||||
}
|
||||
|
||||
public void ShortAlert(string message)
|
||||
{
|
||||
ShowAlert(message, SHORT_DELAY);
|
||||
}
|
||||
|
||||
void ShowAlert(string message, double seconds)
|
||||
{
|
||||
var alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert);
|
||||
|
||||
var alertDelay = NSTimer.CreateScheduledTimer(seconds, obj =>
|
||||
{
|
||||
DismissMessage(alert, obj);
|
||||
});
|
||||
|
||||
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
|
||||
}
|
||||
|
||||
void DismissMessage(UIAlertController alert, NSTimer alertDelay)
|
||||
{
|
||||
if (alert != null)
|
||||
{
|
||||
alert.DismissViewController(true, null);
|
||||
}
|
||||
|
||||
if (alertDelay != null)
|
||||
{
|
||||
alertDelay.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ namespace TINK.Model.Settings
|
|||
public const string SETTINGSFILETITLE = "Setting.Json";
|
||||
|
||||
/// <summary> Key of the app version entry. </summary>
|
||||
public const string APPVERIONKEY = "AppVersion";
|
||||
public const string APPVERSIONKEY = "AppVersion";
|
||||
|
||||
/// <summary> Key of the app version entry. </summary>
|
||||
public const string SHOWWHATSNEWKEY = "ShowWhatsNew";
|
||||
|
@ -171,7 +171,7 @@ namespace TINK.Model.Settings
|
|||
|
||||
return targetDictionary.Union(new Dictionary<string, string>
|
||||
{
|
||||
{APPVERIONKEY , JsonConvert.SerializeObject(appVersion, new VersionConverter()) },
|
||||
{APPVERSIONKEY , JsonConvert.SerializeObject(appVersion, new VersionConverter()) },
|
||||
}).ToDictionary(key => key.Key, value => value.Value);
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ namespace TINK.Model.Settings
|
|||
public static Version GetAppVersion(this IDictionary<string, string> settingsJSON)
|
||||
{
|
||||
// Get the version of the app which wrote the settings file.
|
||||
if (!settingsJSON.TryGetValue(APPVERIONKEY, out string appVersion)
|
||||
if (!settingsJSON.TryGetValue(APPVERSIONKEY, out string appVersion)
|
||||
|| string.IsNullOrEmpty(appVersion))
|
||||
{
|
||||
// File holds no entry.
|
||||
|
|
|
@ -298,277 +298,277 @@ namespace TINK.Model
|
|||
},
|
||||
{
|
||||
new Version(3, 0, 203),
|
||||
AppResources.ChangeLog3_0_203
|
||||
AppResources.ChangeLog_3_0_203
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 204),
|
||||
AppResources.ChangeLog3_0_204
|
||||
AppResources.ChangeLog_3_0_204
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 205),
|
||||
AppResources.ChangeLog3_0_205
|
||||
AppResources.ChangeLog_3_0_205
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 206),
|
||||
AppResources.ChangeLog3_0_206
|
||||
AppResources.ChangeLog_3_0_206
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 207),
|
||||
AppResources.ChangeLog3_0_207
|
||||
AppResources.ChangeLog_3_0_207
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 208),
|
||||
AppResources.ChangeLog3_0_208 // Minor fixes.
|
||||
AppResources.ChangeLog_3_0_208 // Minor fixes.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 209),
|
||||
AppResources.ChangeLog3_0_209
|
||||
AppResources.ChangeLog_3_0_209
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 214),
|
||||
AppResources.ChangeLog3_0_214
|
||||
AppResources.ChangeLog_3_0_214
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 215),
|
||||
AppResources.ChangeLog3_0_215
|
||||
AppResources.ChangeLog_3_0_215
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 216),
|
||||
AppResources.ChangeLog3_0_216
|
||||
AppResources.ChangeLog_3_0_216
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 217),
|
||||
AppResources.ChangeLog3_0_217
|
||||
AppResources.ChangeLog_3_0_217
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 218),
|
||||
AppResources.ChangeLog3_0_208
|
||||
AppResources.ChangeLog_3_0_208
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 219),
|
||||
AppResources.ChangeLog3_0_219
|
||||
AppResources.ChangeLog_3_0_219
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 220),
|
||||
AppResources.ChangeLog3_0_220
|
||||
AppResources.ChangeLog_3_0_220
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 222),
|
||||
AppResources.ChangeLog3_0_222
|
||||
AppResources.ChangeLog_3_0_222
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 223),
|
||||
AppResources.ChangeLog3_0_208
|
||||
AppResources.ChangeLog_3_0_208
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 224),
|
||||
AppResources.ChangeLog3_0_224
|
||||
AppResources.ChangeLog_3_0_224
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 225),
|
||||
AppResources.ChangeLog3_0_208
|
||||
AppResources.ChangeLog_3_0_208
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 226),
|
||||
AppResources.ChangeLog3_0_226
|
||||
AppResources.ChangeLog_3_0_226
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 227),
|
||||
AppResources.ChangeLog3_0_227
|
||||
AppResources.ChangeLog_3_0_227
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 228),
|
||||
AppResources.ChangeLog3_0_208
|
||||
AppResources.ChangeLog_3_0_208
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 231),
|
||||
AppResources.ChangeLog3_0_231
|
||||
AppResources.ChangeLog_3_0_231
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 232),
|
||||
AppResources.ChangeLog3_0_232
|
||||
AppResources.ChangeLog_3_0_232
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 234),
|
||||
AppResources.ChangeLog3_0_234
|
||||
AppResources.ChangeLog_3_0_234
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 235),
|
||||
AppResources.ChangeLog3_0_235
|
||||
AppResources.ChangeLog_3_0_235
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 236),
|
||||
AppResources.ChangeLog3_0_236
|
||||
AppResources.ChangeLog_3_0_236
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 237),
|
||||
AppResources.ChangeLog3_0_237
|
||||
AppResources.ChangeLog_3_0_237
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 238),
|
||||
AppResources.ChangeLog3_0_231
|
||||
AppResources.ChangeLog_3_0_231
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 239),
|
||||
AppResources.ChangeLog3_0_239
|
||||
AppResources.ChangeLog_3_0_239
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 240),
|
||||
AppResources.ChangeLog3_0_240
|
||||
AppResources.ChangeLog_3_0_240
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 241),
|
||||
AppResources.ChangeLog3_0_241
|
||||
AppResources.ChangeLog_3_0_241
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 242),
|
||||
AppResources.ChangeLog3_0_242
|
||||
AppResources.ChangeLog_3_0_242
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 243),
|
||||
AppResources.ChangeLog3_0_243
|
||||
AppResources.ChangeLog_3_0_243
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 244),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
AppResources.ChangeLog_3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 245),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
AppResources.ChangeLog_3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 246),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
AppResources.ChangeLog_3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 247),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
AppResources.ChangeLog_3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 248),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
AppResources.ChangeLog_3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 249),
|
||||
AppResources.ChangeLog3_0_249 // Third-party components updated.
|
||||
AppResources.ChangeLog_3_0_249 // Third-party components updated.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 250),
|
||||
AppResources.ChangeLog3_0_250 // Third-party components updated.
|
||||
AppResources.ChangeLog_3_0_250 // Third-party components updated.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 260),
|
||||
// Same info as for version 3.0.251 and 3.0.252
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
AppResources.ChangeLog_3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 263),
|
||||
AppResources.ChangeLog3_0_263
|
||||
AppResources.ChangeLog_3_0_263
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 264),
|
||||
AppResources.ChangeLog3_0_264
|
||||
AppResources.ChangeLog_3_0_264
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 265),
|
||||
AppResources.ChangeLog3_0_265
|
||||
AppResources.ChangeLog_3_0_265
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 266),
|
||||
AppResources.ChangeLog3_0_266
|
||||
AppResources.ChangeLog_3_0_266
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 276),
|
||||
AppResources.ChangeLog3_0_276
|
||||
AppResources.ChangeLog_3_0_276
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 277),
|
||||
AppResources.ChangeLog3_0_277
|
||||
AppResources.ChangeLog_3_0_277
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 279),
|
||||
AppResources.ChangeLog3_0_278 // Addition spelling corrected and missing translation added.
|
||||
AppResources.ChangeLog_3_0_278 // Addition spelling corrected and missing translation added.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 280),
|
||||
AppResources.ChangeLog3_0_280
|
||||
AppResources.ChangeLog_3_0_280
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 281),
|
||||
AppResources.ChangeLog3_0_280
|
||||
AppResources.ChangeLog_3_0_280
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 283),
|
||||
AppResources.ChangeLog3_0_282
|
||||
AppResources.ChangeLog_3_0_282
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 284),
|
||||
AppResources.ChangeLog3_0_284
|
||||
AppResources.ChangeLog_3_0_284
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 285),
|
||||
AppResources.ChangeLog3_0_285
|
||||
AppResources.ChangeLog_3_0_285
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 289),
|
||||
AppResources.ChangeLog3_0_289
|
||||
AppResources.ChangeLog_3_0_289
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 290),
|
||||
AppResources.ChangeLog3_0_290
|
||||
AppResources.ChangeLog_3_0_290
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 297),
|
||||
AppResources.ChangeLog3_0_293
|
||||
AppResources.ChangeLog_3_0_293
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 298),
|
||||
AppResources.ChangeLog3_0_298
|
||||
AppResources.ChangeLog_3_0_298
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 299),
|
||||
AppResources.ChangeLog3_0_299
|
||||
AppResources.ChangeLog_3_0_299
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 300),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
AppResources.ChangeLog_3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 311),
|
||||
AppResources.ChangeLog3_0_301
|
||||
AppResources.ChangeLog_3_0_301
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 312),
|
||||
AppResources.ChangeLog3_0_312
|
||||
AppResources.ChangeLog_3_0_312
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 323),
|
||||
AppResources.ChangeLog3_0_318 // Support for new lock added.
|
||||
AppResources.ChangeLog_3_0_318 // Support for new lock added.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 324),
|
||||
AppResources.ChangeLog3_0_324
|
||||
AppResources.ChangeLog_3_0_324
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 333),
|
||||
AppResources.ChangeLog3_0_326 // Battery level display and input.
|
||||
AppResources.ChangeLog_3_0_326 // Battery level display and input.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 335),
|
||||
AppResources.ChangeLog3_0_326 // Battery level display and input.
|
||||
AppResources.ChangeLog_3_0_326 // Battery level display and input.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 336),
|
||||
AppResources.ChangeLog3_0_335 // Sharee.bike design improved.
|
||||
AppResources.ChangeLog_3_0_335 // Sharee.bike design improved.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 337),
|
||||
AppResources.ChangeLog3_0_337_SB, // New Design
|
||||
AppResources.ChangeLog_3_0_337_SB, // New Design
|
||||
new List<AppFlavor> { AppFlavor.ShareeBike }
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 337),
|
||||
AppResources.ChangeLog3_0_337_MK, // Notice for low battery
|
||||
AppResources.ChangeLog_3_0_337_MK, // Notice for low battery
|
||||
new List<AppFlavor> { AppFlavor.MeinKonrad }
|
||||
},
|
||||
{
|
||||
|
@ -578,22 +578,22 @@ namespace TINK.Model
|
|||
},
|
||||
{
|
||||
new Version(3, 0, 338),
|
||||
AppResources.ChangeLog3_0_338_LB_MK,
|
||||
AppResources.ChangeLog_3_0_338_LB_MK,
|
||||
new List<AppFlavor> { AppFlavor.LastenradBayern, AppFlavor.MeinKonrad }
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 338),
|
||||
AppResources.ChangeLog3_0_338_SB,
|
||||
AppResources.ChangeLog_3_0_338_SB,
|
||||
new List<AppFlavor> { AppFlavor.ShareeBike }
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 339),
|
||||
AppResources.ChangeLog3_0_339_SB_LB,
|
||||
AppResources.ChangeLog_3_0_339_SB_LB,
|
||||
new List<AppFlavor> { AppFlavor.LastenradBayern, AppFlavor.ShareeBike }
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 339),
|
||||
AppResources.ChangeLog3_0_339_MK,
|
||||
AppResources.ChangeLog_3_0_339_MK,
|
||||
new List<AppFlavor> { AppFlavor.MeinKonrad }
|
||||
},
|
||||
{
|
||||
|
@ -671,7 +671,7 @@ namespace TINK.Model
|
|||
},
|
||||
{
|
||||
new Version(3, 0, 356),
|
||||
AppResources.ChangeLog3_0_231
|
||||
AppResources.ChangeLog_3_0_231
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 360),
|
||||
|
@ -703,11 +703,15 @@ namespace TINK.Model
|
|||
string.Format("{0} <br /> {1}", AppResources.ChangeLog_PackageUpdates, AppResources.ChangeLog_MinorBugFixes),
|
||||
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
|
||||
},
|
||||
{
|
||||
{
|
||||
new Version(3, 0, 369),
|
||||
AppResources.ChangeLog_3_0_369_MK_SB,
|
||||
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 370),
|
||||
AppResources.ChangeLog_3_0_370
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary> Manges the whats new information.</summary>
|
||||
|
|
1608
TINKLib/MultilingualResources/AppResources.Designer.cs
generated
1608
TINKLib/MultilingualResources/AppResources.Designer.cs
generated
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -6,6 +6,7 @@ using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
|||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Repository.Response;
|
||||
using TINK.Repository.Response.Stations;
|
||||
|
@ -155,21 +156,21 @@ namespace TINK.Repository
|
|||
|
||||
public Task<ReservationBookingResponse> DoReserveAsync(string bikeId, Uri operatorUri)
|
||||
{
|
||||
throw new System.Exception("Reservierung im Offlinemodus nicht möglich!");
|
||||
throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
}
|
||||
|
||||
public Task<ReservationCancelReturnResponse> DoCancelReservationAsync(string bikeId, Uri operatorUri)
|
||||
{
|
||||
throw new System.Exception("Abbrechen einer Reservierung im Offlinemodus nicht möglich!");
|
||||
throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
}
|
||||
|
||||
public Task<ReservationBookingResponse> CalculateAuthKeysAsync(string bikeId, Uri operatorUri)
|
||||
=> throw new System.Exception("Schlosssuche im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
public Task<ResponseBase> StartReturningBike(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> throw new System.Exception("Benachrichtigung von start der Rückgabe im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
public Task<ReservationBookingResponse> UpdateLockingStateAsync(
|
||||
string bikeId,
|
||||
|
@ -178,10 +179,10 @@ namespace TINK.Repository
|
|||
LocationDto geolocation,
|
||||
double batteryLevel,
|
||||
IVersionInfo versionInfo)
|
||||
=> throw new System.Exception("Aktualisierung des Schlossstatuses im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
public Task<ReservationBookingResponse> DoBookAsync(Uri operatorUri, string bikeId, Guid guid, double batteryPercentage, LockingAction? nextAction = null)
|
||||
=> throw new System.Exception("Buchung im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
/// <summary> Books a bike and starts opening bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
|
@ -190,7 +191,7 @@ namespace TINK.Repository
|
|||
public Task<ReservationBookingResponse> BookAvailableAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> throw new System.Exception("Buchung von verfügbarem Rad mit Start von Schlossöffnen ist im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
/// <summary> Books a bike and starts opening bike. </summary>
|
||||
/// <param name="bikeId">Id of the bike to book.</param>
|
||||
|
@ -199,13 +200,13 @@ namespace TINK.Repository
|
|||
public Task<ReservationBookingResponse> BookReservedAndStartOpeningAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> throw new System.Exception("Buchung von reserviertem Rad mit Start von Schlossöffnen ist im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
public Task<DoReturnResponse> DoReturn(
|
||||
string bikeId,
|
||||
LocationDto geolocation,
|
||||
Uri operatorUri)
|
||||
=> throw new System.Exception("Rückgabe im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
/// <summary> Returns a bike and starts closing. </summary>
|
||||
/// <param name="bikeId">Id of the bike to return.</param>
|
||||
|
@ -214,24 +215,24 @@ namespace TINK.Repository
|
|||
public Task<DoReturnResponse> ReturnAndStartClosingAsync(
|
||||
string bikeId,
|
||||
Uri operatorUri)
|
||||
=> throw new System.Exception("Rückgabe mit Schloss schließen Befehl im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, int? currentChargeBars, string message, bool isBikeBroken, Uri operatorUri) =>
|
||||
throw new System.Exception("Übermittlung von Feedback im Offlinemodus nicht möglich!");
|
||||
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, int? currentChargeBars, string message, bool isBikeBroken, Uri operatorUri)
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
/// <summary> Submits mini survey to copri server. </summary>
|
||||
/// <param name="answers">Collection of answers.</param>
|
||||
public Task<ResponseBase> DoSubmitMiniSurvey(IDictionary<string, string> answers)
|
||||
=> throw new System.Exception("Übermittlung von der Miniumfrage im Offlinemodus nicht möglich!");
|
||||
=> throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
|
||||
public Task<AuthorizationResponse> DoAuthorizationAsync(string p_strMailAddress, string p_strPassword, string p_strDeviceId)
|
||||
{
|
||||
throw new System.Exception("Anmelden im Offlinemodus nicht möglich!");
|
||||
throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
}
|
||||
|
||||
public Task<AuthorizationoutResponse> DoAuthoutAsync()
|
||||
{
|
||||
throw new System.Exception("Abmelden im Offlinemodus nicht möglich!");
|
||||
throw new System.Exception(AppResources.ErrorNoWeb);
|
||||
}
|
||||
|
||||
public async Task<BikesAvailableResponse> GetBikesAvailableAsync()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using TINK.MultilingualResources;
|
||||
using TINK.MultilingualResources;
|
||||
|
||||
namespace TINK.Repository.Exception
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ namespace TINK.Repository.Exception
|
|||
/// <summary>Constructs a authorization exceptions. </summary>
|
||||
/// <param name="mail">Mail address to create a detailed error message.</param>
|
||||
public InvalidAuthorizationResponseException(string mail, Response.ResponseBase response) :
|
||||
base(string.Format(AppResources.ErrorMessageInvalidAuthorizationResponseException, mail), response)
|
||||
base(string.Format(AppResources.ErrorAccountInvalidAuthorization, mail), response)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
using TINK.MultilingualResources;
|
||||
using TINK.MultilingualResources;
|
||||
|
||||
namespace TINK.Repository.Exception
|
||||
{
|
||||
public class WebConnectFailureException : CommunicationException
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a hint to fix communication problem.
|
||||
/// </summary>
|
||||
public static string GetHintToPossibleExceptionsReasons
|
||||
=> AppResources.ExceptionTextWebConnectFailureException;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a communication exeption object.
|
||||
/// Constructs a communication exception object.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="exception"></param>
|
||||
|
|
|
@ -128,13 +128,23 @@ namespace TINK.Repository.Request
|
|||
/// <summary> Copri locking states</summary>
|
||||
public enum lock_state
|
||||
{
|
||||
/// <summary> Request to backend to close lock in context of pausing ride.</summary>
|
||||
locking,
|
||||
|
||||
/// <summary> Lock is closed.</summary>
|
||||
locked,
|
||||
|
||||
/// <summary> Request to backend to close lock either in context of resuming ride or starting a rental.</summary>
|
||||
unlocking,
|
||||
|
||||
/// <summary> Lock is open.</summary>
|
||||
unlocked,
|
||||
|
||||
/// <summary> Lock is unknown state.</summary>
|
||||
unspecific,
|
||||
}
|
||||
|
||||
/// <summary> Holds lockation info.</summary>
|
||||
/// <summary> Holds location info.</summary>
|
||||
public class LocationDto
|
||||
{
|
||||
public double Latitude { get; private set; }
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace TINK.Repository.Request
|
|||
}
|
||||
|
||||
/// <summary> Gets the smart device parameters. </summary>
|
||||
/// <returns>in a format which is urlencode invariant.</returns>
|
||||
/// <returns>in a format which is url encode invariant.</returns>
|
||||
public static string GetSmartDeviceParameters(this ISmartDevice smartDevice)
|
||||
=> smartDevice != null
|
||||
? $"{(!string.IsNullOrEmpty(smartDevice.Manufacturer) ? $"&user_device_manufacturer={WebUtility.UrlEncode(smartDevice.Manufacturer)}" : string.Empty)}" +
|
||||
|
@ -42,5 +42,28 @@ namespace TINK.Repository.Request
|
|||
$"{(!string.IsNullOrEmpty(smartDevice.VersionText) ? $"&user_device_version={WebUtility.UrlEncode(smartDevice.VersionText)}" : string.Empty)}" +
|
||||
$"{(!string.IsNullOrEmpty(smartDevice.Identifier) ? $"&user_device_id={WebUtility.UrlEncode(smartDevice.Identifier)}" : string.Empty)}"
|
||||
: string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Converts from one locking state enum to another.
|
||||
/// </summary>
|
||||
/// <param name="state">Locking state to convert.</param>
|
||||
/// <returns>Target state.</returns>
|
||||
public static lock_state? GetLockState(this Model.Bikes.BikeInfoNS.BluetoothLock.LockingState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case Model.Bikes.BikeInfoNS.BluetoothLock.LockingState.Open:
|
||||
return lock_state.unlocked;
|
||||
|
||||
case Model.Bikes.BikeInfoNS.BluetoothLock.LockingState.Closed:
|
||||
return lock_state.locked;
|
||||
|
||||
case Model.Bikes.BikeInfoNS.BluetoothLock.LockingState.UnknownFromHardwareError:
|
||||
return lock_state.unspecific;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ namespace TINK.Repository.Response
|
|||
[DataMember]
|
||||
public string co2saving { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Holds information about the returned bike.
|
||||
/// </summary>
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace TINK.Repository.Response
|
|||
this ReservationBookingResponse bookingResponse,
|
||||
string bikeId)
|
||||
{
|
||||
GetIsResponseOk(bookingResponse, string.Format(AppResources.ExceptionTextReservationBikeFailedGeneral, bikeId));
|
||||
GetIsResponseOk(bookingResponse, string.Format(AppResources.ErrorReservingBike, bikeId));
|
||||
|
||||
if (BookingDeclinedException.IsBookingDeclined(bookingResponse.response_state, out BookingDeclinedException exception))
|
||||
{
|
||||
|
@ -99,7 +99,7 @@ namespace TINK.Repository.Response
|
|||
if (bikeInfoRequestedOccupied == null)
|
||||
{
|
||||
throw new System.Exception(string.Format(
|
||||
AppResources.ExceptionTextReservationBikeFailedUnavailalbe,
|
||||
AppResources.ErrorReservingBikeUnavailalbe,
|
||||
bikeId,
|
||||
!string.IsNullOrWhiteSpace(bookingResponse?.response_text) ? $"\r\n{bookingResponse.response_text}" : string.Empty));
|
||||
}
|
||||
|
@ -115,14 +115,14 @@ namespace TINK.Repository.Response
|
|||
this ReservationBookingResponse bookingResponse,
|
||||
string bikeId)
|
||||
{
|
||||
GetIsResponseOk(bookingResponse, string.Format(AppResources.ExceptionTextRentingBikeFailedGeneral, bikeId));
|
||||
GetIsResponseOk(bookingResponse, string.Format(AppResources.ErrorRentingBike, bikeId));
|
||||
|
||||
// Get bike which has to be booked.
|
||||
var bikeInfoRequestedOccupied = bookingResponse?.bikes_occupied?.Values?.FirstOrDefault(x => x.bike == bikeId);
|
||||
if (bikeInfoRequestedOccupied == null)
|
||||
{
|
||||
throw new System.Exception(string.Format(
|
||||
AppResources.ExceptionTextRentingBikeFailedUnavailalbe,
|
||||
AppResources.ErrorRentingBikeUnavailalbe,
|
||||
bikeId,
|
||||
!string.IsNullOrWhiteSpace(bookingResponse?.response_text) ? $"\r\n{bookingResponse.response_text}" : string.Empty));
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ namespace TINK.Repository.Response
|
|||
if (response.response_state.Trim().ToUpper().StartsWith(RESPONSE_AUTHCOOKIE_EXPRIED.ToUpper()))
|
||||
{
|
||||
throw new AuthcookieNotDefinedException(
|
||||
$"{textOfAction}\r\n{AppResources.ExceptionTextSessionExpired}",
|
||||
$"{textOfAction}\r\n{AppResources.ErrorAccountInvalidAuthorization}",
|
||||
response);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace TINK.Services.BluetoothLock
|
|||
var locksInfo = new List<LockInfoTdo>();
|
||||
|
||||
// Add and process locks info object
|
||||
foreach (var bikeInfo in bikes.OfType<Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo>())
|
||||
foreach (var bikeInfo in bikes.OfType<BikeInfo>())
|
||||
{
|
||||
locksInfo.Add(new LockInfoTdo.Builder { Id = bikeInfo.LockInfo.Id, State = null }.Build());
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace TINK.Services.CopriApi.Exception
|
|||
{
|
||||
public class RequestNotCachableException : System.Exception
|
||||
{
|
||||
public RequestNotCachableException(string nameOfAction) : base(AppResources.ErrorNotConnectedToNetwork, new System.Exception($"{nameOfAction} is not cacheable."))
|
||||
public RequestNotCachableException(string nameOfAction) : base(AppResources.ErrorNoWeb, new System.Exception($"{nameOfAction} is not cacheable."))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ namespace TINK.Services.Geolocation
|
|||
|
||||
public class Builder
|
||||
{
|
||||
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
|
||||
public double Latitude { get; set; }
|
||||
|
|
|
@ -23,6 +23,11 @@
|
|||
<Target Name="MATPrerequisite" BeforeTargets="PrepareForBuild" Condition="!Exists('$(MSBuildExtensionsPath)\Microsoft\Multilingual App Toolkit\Microsoft.Multilingual.ResxResources.targets')" Label="MultilingualAppToolkit">
|
||||
<Warning Text="$(MSBuildProjectFile) is Multilingual build enabled, but the Multilingual App Toolkit is unavailable during the build. If building with Visual Studio, please check to ensure that toolkit is properly installed." />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Model\Message\**" />
|
||||
<EmbeddedResource Remove="Model\Message\**" />
|
||||
<None Remove="Model\Message\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MonkeyCache" Version="1.6.3" />
|
||||
<PackageReference Include="MonkeyCache.FileStore" Version="1.6.3" />
|
||||
|
@ -87,4 +92,12 @@
|
|||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="TestShareeLib" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android">
|
||||
<HintPath>..\..\..\..\..\..\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\MonoAndroid\v12.0\Mono.Android.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.iOS">
|
||||
<HintPath>..\..\..\..\..\..\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\Xamarin.iOS\v1.0\Xamarin.iOS.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,18 +1,10 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2027
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.6.33815.320
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TINK", "..\TINK\TINK\TINK.shproj", "{5297504F-603F-4E1A-98AA-57C4A0D9D833}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TINK.Android", "..\TINK\TINK.Android\TINK.Android.csproj", "{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TINK.iOS", "..\TINK\TINK.iOS\TINK.iOS.csproj", "{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TINKLib", "TINKLib.csproj", "{B77F4222-0860-4494-A07C-EE8E09FA9983}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestTINKLib", "..\TestTINKLib\TestTINKLib.csproj", "{730A31A5-6736-43CC-8F84-8FDA5093E283}"
|
||||
EndProject
|
||||
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}"
|
||||
|
@ -70,16 +62,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{AADA3B61-862
|
|||
..\TestData\Set04 - Cancel Booking\001\booking_cancel_bike_7.json = ..\TestData\Set04 - Cancel Booking\001\booking_cancel_bike_7.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestTINK", "..\UITest\TestTINK.csproj", "{240B1AC3-6ECB-4985-BA04-D631668D6CCF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestTINKUI", "..\TestTINKUI\TestTINKUI.csproj", "{B19C892E-2628-4CA7-AD27-08D406A3B14B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestShareeLib", "..\TestShareeLib\TestShareeLib.csproj", "{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
..\TINK\TINK\TINK.projitems*{5297504f-603f-4e1a-98aa-57c4a0d9d833}*SharedItemsImports = 13
|
||||
..\TINK\TINK\TINK.projitems*{62b8950a-70b8-4f9d-affc-0a1ebe7bc9e7}*SharedItemsImports = 4
|
||||
..\TINK\TINK\TINK.projitems*{f2d8208f-a8bf-4403-b0ae-2a1d270e4dc9}*SharedItemsImports = 4
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||
Ad-Hoc|ARM = Ad-Hoc|ARM
|
||||
|
@ -107,110 +92,6 @@ Global
|
|||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|ARM.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|x64.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|x64.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|x86.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Ad-Hoc|x86.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|ARM.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|ARM.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|ARM.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|iPhone.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|x64.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|x64.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|x64.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|x86.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|x86.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.AppStore|x86.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|ARM.Deploy.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|iPhone.Deploy.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|x64.Deploy.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Debug|x86.Deploy.0 = Debug|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|ARM.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|iPhone.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|x64.Deploy.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}.Release|x86.Deploy.0 = Release|Any CPU
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|ARM.ActiveCfg = Ad-Hoc|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|x64.ActiveCfg = Ad-Hoc|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Ad-Hoc|x86.ActiveCfg = Ad-Hoc|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|ARM.ActiveCfg = AppStore|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|x64.ActiveCfg = AppStore|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.AppStore|x86.ActiveCfg = AppStore|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|ARM.ActiveCfg = Debug|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|x64.ActiveCfg = Debug|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Debug|x86.ActiveCfg = Debug|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|Any CPU.ActiveCfg = Release|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|ARM.ActiveCfg = Release|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|x64.ActiveCfg = Release|iPhone
|
||||
{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}.Release|x86.ActiveCfg = Release|iPhone
|
||||
{B77F4222-0860-4494-A07C-EE8E09FA9983}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B77F4222-0860-4494-A07C-EE8E09FA9983}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B77F4222-0860-4494-A07C-EE8E09FA9983}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
|
@ -259,150 +140,54 @@ Global
|
|||
{B77F4222-0860-4494-A07C-EE8E09FA9983}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B77F4222-0860-4494-A07C-EE8E09FA9983}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B77F4222-0860-4494-A07C-EE8E09FA9983}.Release|x86.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|x64.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Ad-Hoc|x86.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|ARM.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|ARM.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|x64.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|x64.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|x86.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.AppStore|x86.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|x64.Build.0 = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{730A31A5-6736-43CC-8F84-8FDA5093E283}.Release|x86.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|x64.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Ad-Hoc|x86.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|ARM.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|ARM.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|x64.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|x64.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|x86.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.AppStore|x86.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{240B1AC3-6ECB-4985-BA04-D631668D6CCF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|x64.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Ad-Hoc|x86.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|ARM.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|ARM.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|x64.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|x64.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|x86.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.AppStore|x86.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{E6AE2E3B-85FF-4737-B1BF-6E9EBF1778D5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -12,9 +12,18 @@
|
|||
<!--Background color-->
|
||||
<Color x:Key="background-color">#BEBEBE</Color>
|
||||
|
||||
<!--Important text color-->
|
||||
<Color x:Key="important-text-color">#D21113</Color>
|
||||
|
||||
<!--Attention color-->
|
||||
<Color x:Key="attention-color">#06D1B1</Color>
|
||||
|
||||
<!--RentalProcess colors-->
|
||||
<Color x:Key="process-step-upcoming">#999999</Color>
|
||||
<Color x:Key="process-step-active">#0D0D0D</Color>
|
||||
<Color x:Key="process-step-succeeded">#5DBF3B</Color>
|
||||
<Color x:Key="process-step-failed">#FC870D</Color>
|
||||
|
||||
<!--Primary Button-->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="WidthRequest" Value="400" />
|
||||
|
|
|
@ -9,9 +9,18 @@
|
|||
<!--Background color-->
|
||||
<Color x:Key="background-color">#BEBEBE</Color>
|
||||
|
||||
<!--Important text color-->
|
||||
<Color x:Key="important-text-color">#BF2828</Color>
|
||||
|
||||
<!--Attention color-->
|
||||
<Color x:Key="attention-color">#FC870D</Color>
|
||||
|
||||
<!--RentalProcess colors-->
|
||||
<Color x:Key="process-step-upcoming">#999999</Color>
|
||||
<Color x:Key="process-step-active">#0D0D0D</Color>
|
||||
<Color x:Key="process-step-succeeded">#5DBF3B</Color>
|
||||
<Color x:Key="process-step-failed">#BF2828</Color>
|
||||
|
||||
<!--Primary Button-->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="WidthRequest" Value="400" />
|
||||
|
|
|
@ -252,18 +252,18 @@ namespace TINK.ViewModel.Account
|
|||
return string.Empty;
|
||||
}
|
||||
|
||||
var bookingStateInfo = string.Format("Aktuell {0} Fahrräder reserviert/ gebucht.", m_iMyBikesCount.Value);
|
||||
var bookingStateInfo = string.Format(AppResources.MarkingAccountReservedBookedBikes, m_iMyBikesCount.Value);
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
// Append offline info
|
||||
return $"{bookingStateInfo} Verbindungsstatus: Offline.";
|
||||
return $"{bookingStateInfo} {AppResources.MarkingAccountConnectionOffline}";
|
||||
}
|
||||
|
||||
if (Exception != null)
|
||||
{
|
||||
// Append offline info
|
||||
return $"{bookingStateInfo} Verbindungstatus: Verbindung unterbrochen. ";
|
||||
return $"{bookingStateInfo} {AppResources.MarkingAccountConnectionInterrupted} ";
|
||||
}
|
||||
|
||||
return bookingStateInfo;
|
||||
|
@ -289,9 +289,9 @@ namespace TINK.ViewModel.Account
|
|||
else
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
"Hinweis",
|
||||
"Bitte mit Internet verbinden zum Verwalten der persönlichen Daten.",
|
||||
"OK");
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -306,10 +306,10 @@ namespace TINK.ViewModel.Account
|
|||
try
|
||||
{
|
||||
// Backup logout message before logout.
|
||||
var l_oMessage = string.Format("Benutzer {0} abgemeldet.", TinkApp.ActiveUser.Mail);
|
||||
var l_oMessage = string.Format(AppResources.MessageLogoutGoodbye, TinkApp.ActiveUser.Mail);
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -320,12 +320,12 @@ namespace TINK.ViewModel.Account
|
|||
catch (UnsupportedCopriVersionDetectedException)
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLogoutErrorTitle,
|
||||
AppResources.ErrorLogoutTitle,
|
||||
string.Format(AppResources.MessageAppVersionIsOutdated, TinkApp.Flavor.GetDisplayName()),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
// Restart polling again.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
|
||||
await m_oViewUpdateManager.StartAsync(Polling.ToImmutable());
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
|
@ -337,17 +337,20 @@ namespace TINK.ViewModel.Account
|
|||
if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
"Verbingungsfehler bei Abmeldung!",
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
AppResources.ErrorLogoutTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
await m_oViewService.DisplayAlert("Fehler bei Abmeldung!", l_oException.Message, "OK");
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.ErrorLogoutTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
|
||||
await m_oViewUpdateManager.StartAsync(Polling.ToImmutable());
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
|
@ -356,8 +359,11 @@ namespace TINK.ViewModel.Account
|
|||
|
||||
TinkApp.ActiveUser.Logout();
|
||||
|
||||
// Display information that log out was perfomrmed.
|
||||
await m_oViewService.DisplayAlert("Auf Wiedersehen!", l_oMessage, "OK");
|
||||
// Display information that log out was performed.
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLogoutGoodbyeTitle,
|
||||
l_oMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
@ -428,7 +434,7 @@ namespace TINK.ViewModel.Account
|
|||
try
|
||||
{
|
||||
// Update bikes at station or my bikes depending on context.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
|
||||
await m_oViewUpdateManager.StartAsync(Polling.ToImmutable());
|
||||
}
|
||||
catch (Exception l_oExcetion)
|
||||
{
|
||||
|
@ -445,15 +451,15 @@ namespace TINK.ViewModel.Account
|
|||
{
|
||||
Log.ForContext<AccountPageViewModel>().Information($"Entering {nameof(OnDisappearing)}...");
|
||||
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
$"Ein unerwarteter Fehler ist aufgetreten. \r\n{l_oException.Message}",
|
||||
"OK");
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
|
@ -72,8 +72,8 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageReservationBikeErrorTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageHintTitle,
|
||||
string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|
@ -84,8 +84,8 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageReservingBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -93,7 +93,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
Log.ForContext<Disposable>().Error("User selected centered bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(AppResources.MessageReservingBikeErrorGeneralTitle, exception.Message, AppResources.MessageAnswerOk);
|
||||
await ViewService.DisplayAlert(AppResources.ErrorReservingBikeTitle, exception.Message, AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = string.Empty; // Todo: Remove this statement because in catch block ActionText is already set to empty above.
|
||||
|
@ -104,7 +104,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
finally
|
||||
{
|
||||
// Restart polling again.
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
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 ActionText is already set to empty.
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before cancel request.
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
BikesViewModel.ActionText = String.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Stornieren der Buchung!",
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorNoWeb,
|
||||
"OK");
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
|
@ -97,7 +97,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
finally
|
||||
{
|
||||
// Restart polling again.
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
// 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 ActionText is already set to empty.
|
||||
|
|
|
@ -202,6 +202,8 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
|
||||
public string StationId => $"Station {Bike.StationId}";
|
||||
|
||||
public string DisplayName => Bike.GetDisplayName();
|
||||
|
||||
public bool IsBikeWithCopriLock => Bike.LockModel == Model.Bikes.BikeInfoNS.BikeNS.LockModel.Sigo;
|
||||
|
||||
/// Returns if type of bike is a cargo pedelec bike.
|
||||
|
|
|
@ -225,5 +225,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
|
||||
public string ErrorText => RequestHandler.ErrorText;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.Logging;
|
||||
using TINK.View;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||
{
|
||||
/// <summary>
|
||||
/// View model for action close bluetooth lock.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class CloseLockActionViewModel<T> : ICloseCommandListener, INotifyPropertyChanged
|
||||
{
|
||||
/// <summary> Notifies view about changes. </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// View model to be used for progress report and unlocking/ locking view.
|
||||
/// </summary>
|
||||
private IBikesViewModel BikesViewModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// View service to show modal notifications.
|
||||
/// </summary>
|
||||
private IViewService ViewService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Service to control locks.
|
||||
/// </summary>
|
||||
private ILocksService LockService { get; }
|
||||
|
||||
/// <summary> Provides a connector object.</summary>
|
||||
protected Func<bool, IConnector> ConnectorFactory { get; }
|
||||
|
||||
/// <summary> Delegate to retrieve connected state. </summary>
|
||||
private Func<bool> IsConnectedDelegate { get; }
|
||||
|
||||
/// <summary>Gets the is connected state. </summary>
|
||||
bool IsConnected;
|
||||
|
||||
/// <summary>Object to start or stop update of view model objects from Copri.</summary>
|
||||
private Func<IPollingUpdateTaskManager> ViewUpdateManager { get; }
|
||||
|
||||
/// <summary> Bike close. </summary>
|
||||
private Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable SelectedBike { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the object.
|
||||
/// </summary>
|
||||
/// <param name="selectedBike">Bike to close.</param>
|
||||
/// <param name="viewUpdateManager">Object to start or stop update of view model objects from Copri.</param>
|
||||
/// <param name="viewService">View service to show modal notifications.</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public CloseLockActionViewModel(
|
||||
Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable selectedBike,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel)
|
||||
{
|
||||
SelectedBike = selectedBike;
|
||||
ViewUpdateManager = viewUpdateManager;
|
||||
ViewService = viewService;
|
||||
BikesViewModel = bikesViewModel
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock progress.
|
||||
/// </summary>
|
||||
/// <param name="step">Current step to process.</param>
|
||||
public void ReportStep(Step step)
|
||||
{
|
||||
switch (step)
|
||||
{
|
||||
case Step.StartStopingPolling:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
break;
|
||||
|
||||
case Step.StartingQueryingLocation:
|
||||
// 1a.Step: Start query geolocation data.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
break;
|
||||
|
||||
case Step.ClosingLock:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
break;
|
||||
|
||||
case Step.WaitStopPollingQueryLocation:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
break;
|
||||
|
||||
case Step.QueryLocationTerminated:
|
||||
break;
|
||||
|
||||
case Step.UpdateLockingState:
|
||||
// 1b.Step: Sent info to backend
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessCloseLockStepUpload;
|
||||
BikesViewModel.RentalProcess.ImportantStepInfoText = AppResources.MarkingRentalProcessCloseLockCheckLock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock state.
|
||||
/// </summary>
|
||||
/// <param name="state">State to process.</param>
|
||||
/// <param name="details">Textual details describing current state.</param>
|
||||
public async Task ReportStateAsync(State state, string details)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case State.OutOfReachError:
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
break;
|
||||
|
||||
case State.CouldntCloseMovingError:
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
break;
|
||||
|
||||
case State.CouldntCloseBoltBlockedError:
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
break;
|
||||
|
||||
case State.GeneralCloseError:
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
details,
|
||||
AppResources.MessageAnswerOk);
|
||||
break;
|
||||
|
||||
case State.WebConnectFailed:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
break;
|
||||
|
||||
case State.ResponseIsInvalid:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
break;
|
||||
|
||||
case State.BackendUpdateFailed:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
// lock GUI
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Stop Updater
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
var stopPollingTask = ViewUpdateManager().StopAsync();
|
||||
|
||||
// Clear logging memory sink to avoid passing log data not related to returning of bike to backend.
|
||||
// Log data is passed to backend when calling CopriCallsHttps.DoReturn().
|
||||
MemoryStackSink.ClearMessages();
|
||||
|
||||
// 1. Step
|
||||
// Parameter for RentalProcess View
|
||||
BikesViewModel.RentalProcess = new RentalProcess(SelectedBike.Id)
|
||||
{
|
||||
State = CurrentRentalProcess.CloseLock,
|
||||
StepIndex = 1,
|
||||
Result = CurrentStepStatus.None
|
||||
};
|
||||
|
||||
// Close Lock
|
||||
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessCloseLockStepCloseLock;
|
||||
BikesViewModel.RentalProcess.ImportantStepInfoText = AppResources.MarkingRentalProcessCloseLockObserve;
|
||||
|
||||
try
|
||||
{
|
||||
#if USELOCALINSTANCE
|
||||
var command = new CloseCommand(SelectedBike, GeolocationService, LockService, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager);
|
||||
await command.Invoke(this);
|
||||
#else
|
||||
await SelectedBike.CloseLockAsync(this, stopPollingTask);
|
||||
#endif
|
||||
Log.ForContext<T>().Information("User locked {bike} successfully.", SelectedBike);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
|
||||
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||||
return;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||||
|
||||
// 2. Step
|
||||
BikesViewModel.RentalProcess.StepIndex = 2;
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.None;
|
||||
BikesViewModel.RentalProcess.ImportantStepInfoText = String.Empty;
|
||||
|
||||
//// Ask if lock is closed
|
||||
//var isLockClosed = await ViewService.DisplayAlert(
|
||||
// AppResources.QuestionRentalProcessCloseLockCheckLockTitle,
|
||||
// AppResources.QuestionRentalProcessCloseLockCheckLockText,
|
||||
// AppResources.QuestionRentalProcessCloseLockCheckLockAnswerYes,
|
||||
// AppResources.QuestionRentalProcessCloseLockCheckLockAnswerNo);
|
||||
|
||||
//// If lock is not closed
|
||||
//if(isLockClosed == false)
|
||||
//{
|
||||
// var retryOrContactresult = await ViewService.DisplayAlert(
|
||||
// AppResources.MessageRentalProcessCloseLockNotClosedTitle,
|
||||
// AppResources.MessageRentalProcessCloseLockNotClosedText,
|
||||
// AppResources.MessageAnswerRetry,
|
||||
// AppResources.MessageAnswerContactSupport);
|
||||
|
||||
// BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
|
||||
// if (retryOrContactresult == true)
|
||||
// {
|
||||
// //restart CloseLock()
|
||||
// }
|
||||
// else if(retryOrContactresult == false)
|
||||
// {
|
||||
// await OpenContactPageAsync();
|
||||
// }
|
||||
//}
|
||||
// If lock is closed
|
||||
//else if(isLockClosed == true)
|
||||
//{
|
||||
IsEndRentalRequested = await ViewService.DisplayAlert(
|
||||
AppResources.QuestionRentalProcessCloseLockEndOrContinueTitle,
|
||||
AppResources.QuestionRentalProcessCloseLockEndOrContinueText,
|
||||
AppResources.QuestionRentalProcessCloseLockEndRentalAnswer,
|
||||
AppResources.QuestionRentalProcessCloseLockContinueRentalAnswer);
|
||||
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||||
|
||||
// Continue with End rental in RequestHandler
|
||||
if (IsEndRentalRequested == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Park bike
|
||||
else if(IsEndRentalRequested == false)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentalProcessCloseLockFinishedTitle,
|
||||
AppResources.MessageRentalProcessCloseLockFinishedText,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
//}
|
||||
|
||||
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary> Opens support. </summary>
|
||||
//#if USEFLYOUT
|
||||
// public void OpenContactPageAsync()
|
||||
//#else
|
||||
// public async Task OpenContactPageAsync()
|
||||
//#endif
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // Open Contact Page with Contact information for operator of SelectedBike
|
||||
//#if USEFLYOUT
|
||||
// ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingFeedbackAndContact);
|
||||
//#else
|
||||
// await ViewService.ShowPage("//ContactPage");
|
||||
//#endif
|
||||
// }
|
||||
// catch (Exception p_oException)
|
||||
// {
|
||||
// Log.Error("Ein unerwarteter Fehler ist auf der Seite Kontakt aufgetreten. Kontext: Klick auf Konakt aufnehmen bei Schloss schließen (Schloss nicht zu!). {@Exception}", p_oException);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// True if user requested End rental.
|
||||
/// </summary>
|
||||
private bool isEndRentalRequested = false;
|
||||
|
||||
/// <summary>
|
||||
/// True if user requested End rental.
|
||||
/// </summary>
|
||||
public bool IsEndRentalRequested
|
||||
{
|
||||
get { return isEndRentalRequested; }
|
||||
set
|
||||
{
|
||||
isEndRentalRequested = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsEndRentalRequested)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,365 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.View;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand;
|
||||
using Serilog;
|
||||
using TINK.Services.BluetoothLock;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||
{
|
||||
/// <summary>
|
||||
/// Return bike action.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of owner.</typeparam>
|
||||
public class EndRentalActionViewModel<T> : IGetLockedLocationCommandListener
|
||||
{
|
||||
/// <summary>
|
||||
/// View model to be used for progress report and unlocking/ locking view.
|
||||
/// </summary>
|
||||
private IBikesViewModel BikesViewModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// View service to show modal notifications.
|
||||
/// </summary>
|
||||
private IViewService ViewService { get; }
|
||||
|
||||
/// <summary>Object to start or stop update of view model objects from Copri.</summary>
|
||||
private Func<IPollingUpdateTaskManager> ViewUpdateManager { get; }
|
||||
|
||||
/// <summary> Bike close. </summary>
|
||||
private Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable SelectedBike { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Service to control locks.
|
||||
/// </summary>
|
||||
private ILocksService LockService { get; }
|
||||
|
||||
/// <summary> Provides a connector object.</summary>
|
||||
protected Func<bool, IConnector> ConnectorFactory { get; }
|
||||
|
||||
/// <summary> Delegate to retrieve connected state. </summary>
|
||||
private Func<bool> IsConnectedDelegate { get; }
|
||||
|
||||
/// <summary>Gets the is connected state. </summary>
|
||||
bool IsConnected;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the object.
|
||||
/// </summary>
|
||||
/// <param name="selectedBike">Bike to close.</param>
|
||||
/// <param name="viewUpdateManager">Object to start or stop update of view model objects from Copri.</param>
|
||||
/// <param name="viewService">View service to show modal notifications.</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public EndRentalActionViewModel(
|
||||
Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
ILocksService lockService,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel)
|
||||
{
|
||||
SelectedBike = selectedBike;
|
||||
IsConnectedDelegate = isConnectedDelegate;
|
||||
ConnectorFactory = connectorFactory;
|
||||
LockService = lockService
|
||||
?? throw new ArgumentException($"Can not construct {typeof(EndRentalActionViewModel<T>)}-object. Parameter {nameof(lockService)} must not be null.");
|
||||
ViewUpdateManager = viewUpdateManager;
|
||||
ViewService = viewService;
|
||||
BikesViewModel = bikesViewModel
|
||||
?? throw new ArgumentException($"Can not construct {typeof(EndRentalActionViewModel<T>)}-object. {nameof(bikesViewModel)} must not be null.");
|
||||
|
||||
// Set parameter for RentalProcess View to initial value.
|
||||
BikesViewModel.RentalProcess = new RentalProcess(SelectedBike.Id)
|
||||
{
|
||||
State = CurrentRentalProcess.None,
|
||||
StepIndex = 0,
|
||||
Result = CurrentStepStatus.None
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the get lock location progress.
|
||||
/// </summary>
|
||||
/// <param name="step">Current step to process.</param>
|
||||
public void ReportStep(Step step)
|
||||
{
|
||||
switch (step)
|
||||
{
|
||||
case Step.StartingQueryLocation:
|
||||
// 1.Step: Geolocation data
|
||||
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessEndRentalStepGPS;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
break;
|
||||
|
||||
case Step.DisconnectingLockOnDisconnectedNoLocationError:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the get lock location state.
|
||||
/// </summary>
|
||||
/// <param name="state">State to process.</param>
|
||||
/// <param name="details">Textual details describing current state.</param>
|
||||
public async Task ReportStateAsync(State state, string details)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case State.DisconnetedNoLocationError:
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
AppResources.ErrorEndRentalNotAtSuitableStation,
|
||||
AppResources.MessageAnswerOk);
|
||||
break;
|
||||
|
||||
case State.DisconnectError:
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
break;
|
||||
|
||||
case State.QueryLocationSucceeded:
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||||
break;
|
||||
|
||||
case State.QueryLocationFailed:
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
AppResources.ErrorNoLocationPermission,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task EndRentalAsync()
|
||||
{
|
||||
Log.ForContext<T>().Information("User requests to return bike {bike}.", SelectedBike);
|
||||
|
||||
// lock GUI
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Stop Updater
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// 1. Step
|
||||
// Parameter for RentalProcess View
|
||||
BikesViewModel.RentalProcess = new RentalProcess(SelectedBike.Id)
|
||||
{
|
||||
State = CurrentRentalProcess.EndRental,
|
||||
StepIndex = 1,
|
||||
Result = CurrentStepStatus.None
|
||||
};
|
||||
|
||||
// Get Location
|
||||
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessEndRentalStepGPS;
|
||||
BikesViewModel.RentalProcess.ImportantStepInfoText = AppResources.MarkingRentalProcessEndRentalWait;
|
||||
|
||||
LocationDto currentLocationDto = null;
|
||||
try
|
||||
{
|
||||
currentLocationDto = await SelectedBike.GetLockedBikeLocationAsync(this);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||||
return;
|
||||
}
|
||||
|
||||
// Send end of rental to backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
BookingFinishedModel bookingFinished;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
try
|
||||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
currentLocationDto);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
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);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
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);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
string.Format(AppResources.ErrorEndRentalNotAtStation, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
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);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
string.Format(AppResources.ErrorEndRentalUnknownLocation),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
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);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||||
return;
|
||||
}
|
||||
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||||
|
||||
// 2.Step: User feedback on bike condition
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
|
||||
BikesViewModel.RentalProcess.StepIndex = 2;
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.None;
|
||||
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessEndRentalStepFeedback;
|
||||
BikesViewModel.RentalProcess.ImportantStepInfoText = String.Empty;
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery);
|
||||
#endif
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||||
|
||||
// 3.Step
|
||||
|
||||
// Send user feedback to backend
|
||||
BikesViewModel.RentalProcess.StepIndex = 3;
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.None;
|
||||
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessEndRentalStepUpload;
|
||||
BikesViewModel.RentalProcess.ImportantStepInfoText = AppResources.MarkingRentalProcessEndRentalWait;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto
|
||||
{
|
||||
BikeId = SelectedBike.Id,
|
||||
CurrentChargeBars = feedback.CurrentChargeBars,
|
||||
IsBikeBroken = feedback.IsBikeBroken,
|
||||
Message = feedback.Message
|
||||
},
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorSubmitFeedbackTitle,
|
||||
AppResources.ErrorSubmitFeedback,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
#endif
|
||||
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||||
|
||||
// Disconnect lock.
|
||||
try
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Confirmation message that rental is ended
|
||||
Log.ForContext<T>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
String.Format(AppResources.MessageRentalProcessEndRentalFinishedTitle, SelectedBike.Id),
|
||||
String.Format(
|
||||
"{0}{1}",
|
||||
!string.IsNullOrWhiteSpace(bookingFinished?.Co2Saving) ?
|
||||
$"{bookingFinished?.Co2Saving}\r\n\r\n"
|
||||
: string.Empty,
|
||||
String.Format(AppResources.MessageRentalProcessEndRentalFinishedText)
|
||||
),
|
||||
AppResources.MessageAnswerOk
|
||||
);
|
||||
|
||||
// Mini survey
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||||
BikesViewModel.IsIdle = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,8 +37,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. Parameter {nameof(lockService)} must not be null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service to query geolocation information.
|
||||
/// </summary>
|
||||
protected IGeolocationService GeolocationService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Service to control locks.
|
||||
/// </summary>
|
||||
protected ILocksService LockService { get; }
|
||||
|
||||
public string LockitButtonText { get; protected set; }
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
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;
|
||||
using TINK.View;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand;
|
||||
using static TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
public class BookedClosed : Base, IRequestHandler
|
||||
public class BookedClosed : Base, IRequestHandler, IGetLockedLocationCommandListener
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
|
@ -48,312 +46,54 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
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.
|
||||
|
||||
_endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
|
||||
selectedBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
lockService,
|
||||
viewUpdateManager,
|
||||
viewService,
|
||||
bikesViewModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the view model for end rental action.
|
||||
/// </summary>
|
||||
private EndRentalActionViewModel<BookedClosed> _endRentalActionViewModel;
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReturnBike();
|
||||
public async Task<IRequestHandler> HandleRequestOption1()
|
||||
{
|
||||
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> HandleRequestOption2() => await OpenLock();
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> ReturnBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<IGeolocation> currentLocationTask = null;
|
||||
/// <summary>
|
||||
/// Processes the get lock location progress.
|
||||
/// </summary>
|
||||
/// <param name="step">Current step to process.</param>
|
||||
public void ReportStep(Step step) => _endRentalActionViewModel.ReportStep(step);
|
||||
|
||||
// Try getting geolocation which was requested when closing lock.
|
||||
IGeolocation currentLocation = SelectedBike.LockInfo.Location;
|
||||
var lastConfimredLockStateTimeStamp = SelectedBike.LockInfo.LastLockingStateChange;
|
||||
|
||||
// Check if bike is around.
|
||||
var deviceState = LockService[SelectedBike.LockInfo.Id].GetDeviceState();
|
||||
|
||||
// Check if
|
||||
// - geolocation is already available
|
||||
// - or if bike is in reach so that geolocation makes sense
|
||||
if (currentLocation == null && deviceState != DeviceState.Connected)
|
||||
{
|
||||
// Geolocation information is missing and can not be queried.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
AppResources.Error_ReturnBike_Station_Location_Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Check if querying geolocation is required.
|
||||
if (currentLocation == null)
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
|
||||
// Start getting geolocation.
|
||||
try
|
||||
{
|
||||
currentLocationTask = GeolocationService.GetAsync(ctsLocation.Token, DateTime.Now);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedClosed>().Information("Getting geolocation when returning bike {Bike} failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
AppResources.ErrorReturnBikeLockClosedStartGetGPSExceptionMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User aborted returning bike process
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {l_oId} in order to return but action was canceled.", SelectedBike.Id);
|
||||
|
||||
// Cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedClosed>().Information("Canceling query location failed on abort returning closed bike failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<BookedClosed>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Get geolocation if
|
||||
// - geolocation was not available when closing lock
|
||||
// - bike is around (lock is connected via bluetooth)
|
||||
LocationDto currentLocationDto = null;
|
||||
if (currentLocation == null)
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedClosed>().Information("Returning closed bike {Bike} is not possible. Cancel geolocation query failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorQueryGeolocation,
|
||||
AppResources.ErrorReturnBikeLockClosedGetGPSExceptionMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
lastConfimredLockStateTimeStamp = DateTime.Now;
|
||||
}
|
||||
|
||||
currentLocationDto = currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = lastConfimredLockStateTimeStamp is DateTime lastLockState ? lastLockState.Subtract(currentLocation.Timestamp.DateTime) : TimeSpan.MaxValue,
|
||||
}.Build()
|
||||
: null;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
BookingFinishedModel bookingFinished;
|
||||
try
|
||||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
currentLocationDto);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information(
|
||||
"User selected booked bike {bike} but returning failed. COPRI returned out of GEO fencing error. Position send to COPRI {@position}.",
|
||||
SelectedBike,
|
||||
currentLocationDto);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockClosedNoGPSMessage),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
"OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedClosed>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery, bookingFinished?.Co2Saving);
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto
|
||||
{
|
||||
BikeId = SelectedBike.Id,
|
||||
CurrentChargeBars = feedback.CurrentChargeBars,
|
||||
IsBikeBroken = feedback.IsBikeBroken,
|
||||
Message = feedback.Message
|
||||
},
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
#endif
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
/// <summary>
|
||||
/// Processes the get lock location state.
|
||||
/// </summary>
|
||||
/// <param name="state">State to process.</param>
|
||||
/// <param name="details">Textual details describing current state.</param>
|
||||
public async Task ReportStateAsync(State state, string details) => await _endRentalActionViewModel.ReportStateAsync(state, details);
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
|
@ -364,7 +104,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
|
@ -383,7 +123,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
|
@ -392,7 +132,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldIsBlockedMessage,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
|
@ -400,9 +140,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillClosedTitle,
|
||||
AppResources.ErrorOpenLockBoldStatusIsUnknownMessage,
|
||||
"OK");
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
|
@ -411,17 +151,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
"OK");
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// When bold is blocked lock is still closed even if exception occurs.
|
||||
|
@ -431,9 +172,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -501,9 +243,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
Log.ForContext<BookedClosed>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQuerryServer;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -93,8 +93,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BookedDisconnected>().Information("User selected booked bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
$"{AppResources.ErrorConnectLockRentedBikeNoWebMessage}\r\n{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}",
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -102,14 +102,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BookedDisconnected>().Error("User selected booked bike {l_oId} to connect to lock. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
$"{AppResources.ErrorConnectLockGeneralErrorMessage}\r\n{exception.Message}",
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
|
@ -137,8 +137,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = false;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
AppResources.ErrorFindLockBluetoothNotOn,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockBluetoothNotOn,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ConnectLocationPermissionMissingException)
|
||||
|
@ -146,8 +146,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = false;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
AppResources.ErrorFindLockLocationPermissionMissing,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorNoLocationPermission,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ConnectLocationOffException)
|
||||
|
@ -155,19 +155,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = false;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
AppResources.ErrorFindLockLocationOff,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockLocationOff,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Debug("Lock can not be found because out of reach. {Exception}", exception);
|
||||
continueConnect = false;
|
||||
|
||||
continueConnect = await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
AppResources.ErrorFindLockRentedBikeOutOfReachMessage,
|
||||
AppResources.MessageAnswerRetry,
|
||||
AppResources.MessageAnswerCancel);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -176,22 +175,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
string message;
|
||||
if (retryCount < 2)
|
||||
{
|
||||
message = AppResources.ErrorBookedSearchMessage;
|
||||
message = AppResources.ErrorConnectLock;
|
||||
}
|
||||
else if (retryCount < 3)
|
||||
{
|
||||
message = AppResources.ErrorBookedSearchMessageEscalationLevel1;
|
||||
message = AppResources.ErrorConnectLockEscalationLevel1;
|
||||
}
|
||||
else
|
||||
{
|
||||
message = AppResources.ErrorBookedSearchMessageEscalationLevel2;
|
||||
message = AppResources.ErrorConnectLockEscalationLevel2;
|
||||
}
|
||||
|
||||
|
||||
continueConnect = await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
message,
|
||||
"", // bool IsReportLevelVerbose ? exception.Message : string.Empty, // or use ActiveUser.DebugLevel.HasFlag(Permissions.ReportLevel) instead?
|
||||
"",
|
||||
AppResources.MessageAnswerRetry,
|
||||
AppResources.MessageAnswerCancel);
|
||||
}
|
||||
|
@ -204,32 +203,32 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Quit and restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
if (result?.State == null)
|
||||
if (!(result?.State is LockitLockingState lockingState))
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
$"Schlossstatus des gemieteten Rads konnte nicht ermittelt werden.",
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockNoStatus,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
var state = result.State.Value.GetLockingState();
|
||||
var state = lockingState.GetLockingState();
|
||||
SelectedBike.LockInfo.State = state;
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
|
@ -237,7 +236,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
|
@ -9,13 +7,11 @@ using TINK.Model.Connector;
|
|||
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;
|
||||
using TINK.Services.Logging;
|
||||
using TINK.View;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
|
||||
using static TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -34,8 +30,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseAndReturn, // Copri button text: "Schloss schließen & Miete beenden"
|
||||
true, // Show button to allow user to return bike.
|
||||
AppResources.ActionClose, // Copri button text: "Close lock"
|
||||
true, // Show button to allow user to close lock.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -46,634 +42,92 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen".
|
||||
IsLockitButtonVisible = true; // Show button to allow user to lock bike.
|
||||
LockitButtonText = string.Empty;
|
||||
IsLockitButtonVisible = false;
|
||||
|
||||
_closeLockActionViewModel = new CloseLockActionViewModel<BookedOpen>(
|
||||
selectedBike,
|
||||
viewUpdateManager,
|
||||
viewService,
|
||||
bikesViewModel);
|
||||
}
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockAndReturnBike();
|
||||
/// <summary>
|
||||
/// Holds the view model for close action.
|
||||
/// </summary>
|
||||
private CloseLockActionViewModel<BookedOpen> _closeLockActionViewModel;
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> CloseLockAndReturnBike()
|
||||
/// <summary> Close lock (and return bike).</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1()
|
||||
{
|
||||
// Prevent concurrent interaction
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<IGeolocation> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
await _closeLockActionViewModel.CloseLockAsync();
|
||||
if(_closeLockActionViewModel.IsEndRentalRequested == false)
|
||||
{
|
||||
currentLocationTask = GeolocationService.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorQueryLocationStartTitle,
|
||||
$"{AppResources.MessageErrorQueryLocationMessage}\r\n{ex.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // 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);
|
||||
}
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCloseLockAndReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (result == false)
|
||||
{
|
||||
// User aborted closing and returning bike process
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {l_oId} in order to close and return but action was canceled.", SelectedBike.Id);
|
||||
|
||||
// Cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on abort returning opened bike failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Start of closing lock and returning bike sequence.
|
||||
Log.ForContext<BookedOpen>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
|
||||
// Clear logging memory sink to avoid passing log data not related to returning of bike to back-end.
|
||||
// Log data is passed to back end when calling CopriCallsHttps.DoReturn().
|
||||
MemoryStackSink.ClearMessages();
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Notify COPRI about start returning bike sequence: "request=booking_update ... &lock_state=locking ..."
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartReturningBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.StartReturningBike(
|
||||
SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returning failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())
|
||||
?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
Task updateLockingStateTask = Task.CompletedTask;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
updateLockingStateTask = ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike);
|
||||
}
|
||||
catch (Exception innerExceptionStartUpdateLockingState)
|
||||
{
|
||||
// No location information available/ updating state failed.
|
||||
Log.ForContext<BookedOpen>().Information("Start update locking state failed on lock operating error. {Exception}", SelectedBike, innerExceptionStartUpdateLockingState);
|
||||
}
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(new List<Task> { currentLocationTask ?? Task.CompletedTask, updateLockingStateTask });
|
||||
}
|
||||
catch (Exception innerExWhenAll)
|
||||
{
|
||||
// No location information available/ updating state failed.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location/ updating lock state failed on closing lock error. {Exception}", SelectedBike, innerExWhenAll);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // 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);
|
||||
}
|
||||
|
||||
// Check locking state.
|
||||
if (SelectedBike.LockInfo.State != LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error($"Lock can not be closed. Invalid locking state {SelectedBike.LockInfo.State} detected.");
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
// Notify COPRI about closing failure: "request=booking_update ... &lock_state=unlocked ..."
|
||||
Task updateLockingStateTask = Task.CompletedTask;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
updateLockingStateTask = ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike);
|
||||
}
|
||||
catch (Exception innerExceptionStartUpdateLockingState)
|
||||
{
|
||||
// No location information available/ updating state failed.
|
||||
Log.ForContext<BookedOpen>().Information("Start update locking state failed on unexpected state. {Exception}", SelectedBike, innerExceptionStartUpdateLockingState);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
SelectedBike.LockInfo.State == LockingState.Open
|
||||
? AppResources.ErrorCloseLockStillOpenMessage
|
||||
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(new List<Task> { currentLocationTask ?? Task.CompletedTask, updateLockingStateTask });
|
||||
}
|
||||
catch (Exception innerExWhenAll)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location/ updating lock state failed on unexpected lock state failed. {Exception}", SelectedBike, innerExWhenAll);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // 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 geolocation information.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
IGeolocation currentLocation = null;
|
||||
try
|
||||
{
|
||||
var task = await Task.WhenAny(new List<Task> { currentLocationTask });
|
||||
currentLocation = currentLocationTask.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageErrorQueryLocationTitle,
|
||||
AppResources.MessageErrorQueryLocationMessage,
|
||||
ex.GetErrorMessage(),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // 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);
|
||||
}
|
||||
|
||||
// Notify COPRI about end of rental: "request=booking_update ... "&state=available" ... &lock_state=locked ..."
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
LocationDto currentLocationDto = null;
|
||||
BookingFinishedModel bookingFinished;
|
||||
try
|
||||
{
|
||||
currentLocationDto = currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null;
|
||||
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
return Create(
|
||||
SelectedBike,
|
||||
currentLocationDto,
|
||||
SmartDevice);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information(
|
||||
"User selected booked bike {bike} but returning failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information(
|
||||
"User selected booked bike {bike} but returning failed. COPRI returned out of GEO fencing error. Position send to COPRI {@position}.",
|
||||
SelectedBike,
|
||||
currentLocationDto);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returning failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockOpenNoGPSMessage),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returning failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
GeolocationService,
|
||||
LockService,
|
||||
ViewUpdateManager,
|
||||
SmartDevice,
|
||||
ViewService,
|
||||
BikesViewModel,
|
||||
ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
var _endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
|
||||
SelectedBike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
LockService,
|
||||
ViewUpdateManager,
|
||||
ViewService,
|
||||
BikesViewModel);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
await _endRentalActionViewModel.EndRentalAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(
|
||||
SelectedBike.Drive?.Battery,
|
||||
bookingFinished?.Co2Saving);
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto
|
||||
{
|
||||
BikeId = SelectedBike.Id,
|
||||
CurrentChargeBars = feedback.CurrentChargeBars,
|
||||
IsBikeBroken = feedback.IsBikeBroken,
|
||||
Message = feedback.Message
|
||||
},
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
public Task<IRequestHandler> HandleRequestOption2() => throw new InvalidOperationException();
|
||||
|
||||
/// <summary> Request is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedOpen>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Clear logging memory sink to avoid passing log data not related to returning of bike to back end.
|
||||
// Log data is passed to back end when calling CopriCallsHttps.DoReturn().
|
||||
MemoryStackSink.ClearMessages();
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<IGeolocation> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = GeolocationService.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Closing lock of bike {Bike} is not possible. Starting query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Update current state from exception
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
IGeolocation currentLocation = null;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask });
|
||||
currentLocation = currentLocationTask.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Getting geolocation when closing lock of bike {Bike} failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Keep geolocation where closing action occurred.
|
||||
SelectedBike.LockInfo.Location = currentLocation;
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User locked bike {bike} in order to pause ride but updating failed. Message: {Message} Details: {Details}", SelectedBike, copriException.Message, copriException.Response);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User locked bike {bike} in order to pause ride but updating failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock progress.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only used for testing.
|
||||
/// </remarks>
|
||||
/// <param name="step">Current step to process.</param>
|
||||
public void ReportStep(Step step) => _closeLockActionViewModel?.ReportStep(step);
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only used for testing.
|
||||
/// </remarks>
|
||||
/// <param name="state">State to process.</param>
|
||||
/// <param name="details">Textual details describing current state.</param>
|
||||
public async Task ReportStateAsync(State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ using TINK.Services.BluetoothLock;
|
|||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
|
||||
using static TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -47,14 +49,28 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
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.
|
||||
|
||||
_closeLockActionViewModel = new CloseLockActionViewModel<BookedOpen>(
|
||||
selectedBike,
|
||||
viewUpdateManager,
|
||||
viewService,
|
||||
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 in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
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()
|
||||
|
@ -65,7 +81,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
|
@ -84,7 +100,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
|
@ -93,7 +109,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldIsBlockedMessage,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
|
@ -101,8 +117,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillClosedTitle,
|
||||
AppResources.ErrorOpenLockBoldStatusIsUnknownMessage,
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
|
@ -112,16 +128,17 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -132,7 +149,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -202,178 +219,29 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
Log.ForContext<BookedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
/// <summary>
|
||||
/// Processes the close lock progress.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only used for testing.
|
||||
/// </remarks>
|
||||
/// <param name="step">Current step to process.</param>
|
||||
public void ReportStep(Step step) => _closeLockActionViewModel?.ReportStep(step);
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<IGeolocation> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = GeolocationService.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Canceling query location failed on unexpected lock state failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
IGeolocation currentLocation = null;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Get geolocation failed when closing lock of bike {Bike} with unknown state. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
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);
|
||||
|
||||
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. Message: {Message} Details: {Details}", SelectedBike, copriException.Message, 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);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
/// <summary>
|
||||
/// Processes the close lock state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only used for testing.
|
||||
/// </remarks>
|
||||
/// <param name="state">State to process.</param>
|
||||
/// <param name="details">Textual details describing current state.</param>
|
||||
public async Task ReportStateAsync(State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -101,8 +101,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<DisposableDisconnected>().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageReservationBikeErrorTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageHintTitle,
|
||||
string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|
@ -112,23 +112,24 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<DisposableDisconnected>().Information("User selected centered bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageReservingBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected centered bike {bike} but reserving failed. {@l_oException}", SelectedBike, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageReservingBikeErrorGeneralTitle,
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReservingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
|
@ -160,7 +161,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -174,7 +175,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -212,7 +213,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -236,25 +237,23 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// 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);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
l_oException.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Empty,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -277,7 +276,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
|
@ -286,7 +285,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldIsBlockedMessage,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
|
@ -294,8 +293,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillClosedTitle,
|
||||
AppResources.ErrorOpenLockBoldStatusIsUnknownMessage,
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
|
@ -305,7 +304,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -322,7 +321,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
|
@ -333,7 +332,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
|
@ -404,7 +403,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
Log.ForContext<DisposableDisconnected>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionBookOrClose,
|
||||
AppResources.ActionCloseOrBook,
|
||||
true, // Show copri button to enable reserving
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -76,14 +76,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Ask whether to really book bike or close lock?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
$"Fahrrad {SelectedBike.GetFullDisplayName()} mieten oder Schloss schließen?",
|
||||
"Mieten",
|
||||
"Schloss schließen");
|
||||
String.Format(AppResources.QuestionCloseOrBook, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.ActionBook,
|
||||
AppResources.ActionClose);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
|
@ -112,25 +112,26 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
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.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -139,7 +140,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -159,7 +160,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -206,8 +207,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<DisposableOpen>().Information("User selected requested bike {l_oId} but reserving failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
string.Format(AppResources.MessageErrorLockIsClosedThreeLines, l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -215,13 +216,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<DisposableOpen>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Format(AppResources.MessageErrorLockIsClosedTwoLines, l_oException.Message),
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
string.Format(l_oException.Message, AppResources.ErrorTryAgain),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// If booking failed lock bike again because bike is only reserved.
|
||||
BikesViewModel.ActionText = "Verschließe Schloss...";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
|
@ -250,7 +251,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
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.
|
||||
|
@ -262,7 +263,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
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.
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.State;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||
|
@ -48,10 +49,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
// User is not logged in
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
"Hinweis",
|
||||
"Bitte anmelden vor Reservierung eines Fahrrads!\r\nAuf Anmeldeseite wechseln?",
|
||||
"Ja",
|
||||
"Nein");
|
||||
AppResources.QuestionLogInTitle,
|
||||
AppResources.QuestionLogIn,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
|
|
|
@ -68,8 +68,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.QuestionAnswerYes,
|
||||
AppResources.QuestionAnswerNo);
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
|
@ -83,7 +83,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -101,8 +101,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|
@ -111,21 +111,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// 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);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -147,7 +148,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -177,7 +178,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
|
@ -195,10 +196,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
l_oException.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -206,14 +206,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedClosed>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Empty,
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -234,27 +234,30 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldIsBlockedMessage,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillClosedTitle,
|
||||
AppResources.ErrorOpenLockBoldStatusIsUnknownMessage,
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
|
@ -262,16 +265,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -281,7 +286,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
|
@ -292,7 +297,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
|
@ -363,7 +368,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
Log.ForContext<ReservedClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCancelRequest, // Copri button text: "Reservierung abbrechen"
|
||||
AppResources.ActionCancelRequest,
|
||||
true, // Show button to enable canceling reservation.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -45,7 +45,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
IsLockitButtonVisible = true; // Show button to search lock.
|
||||
}
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
|
@ -63,8 +63,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.QuestionAnswerYes,
|
||||
AppResources.QuestionAnswerNo);
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -94,8 +94,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|
@ -104,21 +104,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// 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);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -127,7 +128,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedDisconnected>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -142,7 +143,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQuerryServer;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -162,23 +163,24 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedDisconnected>().Information("User selected requested bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorConnectTitle,
|
||||
$"{AppResources.ErrorConnectLockReservedBikeNoWebMessage}\r\n{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}",
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected requested bike {l_oId} to scan for lock. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorConnectTitle,
|
||||
$"{AppResources.ErrorConnectLockGeneralErrorMessage}\r\n{exception.Message}",
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorConnectLock,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
|
@ -212,8 +214,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = false;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorConnectTitle,
|
||||
AppResources.ErrorFindLockBluetoothNotOn,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockBluetoothNotOn,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ConnectLocationPermissionMissingException)
|
||||
|
@ -221,8 +223,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = false;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
AppResources.ErrorFindLockLocationPermissionMissing,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorNoLocationPermission,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ConnectLocationOffException)
|
||||
|
@ -230,19 +232,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = false;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
AppResources.ErrorFindLockLocationOff,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockLocationOff,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be found because out of reach.. {Exception}", exception);
|
||||
continueConnect = false;
|
||||
|
||||
continueConnect = await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorConnectTitle,
|
||||
AppResources.ErrorFindLockReservedBikeOutOfReachMessage,
|
||||
AppResources.MessageAnswerRetry,
|
||||
AppResources.MessageAnswerCancel);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -251,18 +252,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
string message;
|
||||
if (retryCount < 2)
|
||||
{
|
||||
message = AppResources.ErrorReservedSearchMessage;
|
||||
message = AppResources.ErrorConnectLock;
|
||||
}
|
||||
else
|
||||
{
|
||||
message = AppResources.ErrorReservedSearchMessageEscalationLevel1;
|
||||
message = AppResources.ErrorConnectLockEscalationLevel1;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock state can not be retrieved. {Exception}", exception);
|
||||
continueConnect = await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageErrorConnectTitle,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
message,
|
||||
"", // bool IsReportLevelVerbose ? exception.Message : string.Empty, // or use ActiveUser.DebugLevel.HasFlag(Permissions.ReportLevel) instead?
|
||||
"", // Might show detailed info in future versions. Property used earlier: IsReportLevelVerbose. Maybe use ActiveUser.DebugLevel.HasFlag(Permissions.ReportLevel) instead.
|
||||
AppResources.MessageAnswerRetry,
|
||||
AppResources.MessageAnswerCancel);
|
||||
}
|
||||
|
@ -275,32 +276,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
if (result?.State == null)
|
||||
if (!(result?.State is LockitLockingState lockingState))
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorConnectTitle,
|
||||
AppResources.ErrorFindLockReservedBikeNoStausMessage,
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockNoStatus,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
var state = result.State.Value.GetLockingState();
|
||||
SelectedBike.LockInfo.State = state;
|
||||
SelectedBike.LockInfo.State = lockingState.GetLockingState();
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information($"State for bike {SelectedBike.Id} updated successfully. Value is {SelectedBike.LockInfo.State}.");
|
||||
|
@ -334,7 +334,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -358,10 +358,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// 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);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
l_oException.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -369,14 +368,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedDisconnected>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Empty,
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -399,7 +398,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
|
@ -408,7 +407,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldIsBlockedMessage,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
|
@ -416,8 +415,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillClosedTitle,
|
||||
AppResources.ErrorOpenLockBoldStatusIsUnknownMessage,
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
|
@ -427,15 +426,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock can not be opened. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -444,7 +444,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
|
@ -455,7 +455,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
|
@ -528,7 +528,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
"Rad zurückgeben oder mieten",
|
||||
AppResources.ActionCloseOrBook,
|
||||
true, // Show button to enable canceling reservation.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -67,13 +67,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format("Rad {0} abschließen und zurückgeben oder Rad mieten?", SelectedBike.GetFullDisplayName()),
|
||||
"Zurückgeben",
|
||||
"Mieten");
|
||||
string.Format(AppResources.QuestionCloseOrBook, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.ActionClose,
|
||||
AppResources.ActionBook);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
|
@ -118,22 +118,23 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedOpen>().Information("User selected requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
string.Format(AppResources.MessageErrorLockIsClosedThreeLines, l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
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.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Format(AppResources.MessageErrorLockIsClosedTwoLines, l_oException.Message),
|
||||
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 = "Wiederverschließe Schloss...";
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
|
@ -148,7 +149,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
|
@ -158,7 +159,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedOpen>().Information("User booked bike {bike} successfully.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
|
@ -181,8 +182,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachStateReservedMessage,
|
||||
"OK");
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
|
@ -190,17 +191,17 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
"OK");
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
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.ErrorCloseLockBoldBlockedMessage,
|
||||
"OK");
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -208,8 +209,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
string.Format(AppResources.ErrorCloseLockUnkErrorMessage, exception.Message),
|
||||
"OK");
|
||||
string.Format(AppResources.ErrorCloseLock, exception.Message),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
|
@ -217,7 +218,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -238,8 +239,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// 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.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|
@ -248,21 +249,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// 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);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -284,7 +286,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
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);
|
||||
|
@ -296,7 +298,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Close lock
|
||||
Log.ForContext<ReservedOpen>().Information("User selected disposable bike {bike} in order to manage sound/ alarm settings.", SelectedBike);
|
||||
|
@ -316,7 +318,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Abschalten der Sounds!",
|
||||
"Sounds können erst abgeschalten werden, wenn Rad in der Nähe ist.",
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -328,7 +330,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Abschalten der Sounds!",
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -347,7 +349,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Setzen der Alarm-Einstellungen!",
|
||||
"Alarm kann erst eingestellt werden, wenn Rad in der Nähe ist.",
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -359,7 +361,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Setzen der Alarms-Einstellungen!",
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -378,7 +380,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Abschalten des Alarms!",
|
||||
"Alarm kann erst abgeschalten werden, wenn Rad in der Nähe ist.",
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -390,22 +392,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Abschalten des Alarms!",
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
finally
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Hinweis",
|
||||
AppResources.MessageHintTitle,
|
||||
"Alarm und Sounds erfolgreich abgeschalten.",
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,13 @@ 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;
|
||||
using TINK.View;
|
||||
using Xamarin.Essentials;
|
||||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
|
||||
using static TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
|
||||
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -48,6 +49,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
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.
|
||||
|
||||
_closeLockActionViewModel = new CloseLockActionViewModel<BookedOpen>(
|
||||
selectedBike,
|
||||
viewUpdateManager,
|
||||
viewService,
|
||||
bikesViewModel);
|
||||
}
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
|
@ -62,7 +69,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
|
@ -79,27 +86,30 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldIsBlockedMessage,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. lock reports state unkwnown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillClosedTitle,
|
||||
AppResources.ErrorOpenLockBoldStatusIsUnknownMessage,
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
|
@ -107,18 +117,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.ErrorOpenLockBikeAlreadyBooked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -129,7 +141,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -199,182 +211,43 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
Log.ForContext<ReservedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
/// <summary>
|
||||
/// Holds the view model for close action.
|
||||
/// </summary>
|
||||
private CloseLockActionViewModel<BookedOpen> _closeLockActionViewModel;
|
||||
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
/// <summary> Close lock (and return bike).</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<IGeolocation> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = GeolocationService.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
IGeolocation currentLocation = null;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
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);
|
||||
|
||||
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. Message: {Message} Details: {Details}", SelectedBike, copriException.Message, 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);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
await _closeLockActionViewModel.CloseLockAsync();
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock progress.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only used for testing.
|
||||
/// </remarks>
|
||||
/// <param name="step">Current step to process.</param>
|
||||
public void ReportStep(Step step) => _closeLockActionViewModel?.ReportStep(step);
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only used for testing.
|
||||
/// </remarks>
|
||||
/// <param name="state">State to process.</param>
|
||||
/// <param name="details">Textual details describing current state.</param>
|
||||
public async Task ReportStateAsync(State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="viewService"></param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
/// <param name="context">Specifies the context (last action performed).</param>
|
||||
/// <returns>Request handler.</returns>
|
||||
public static IRequestHandler Create(
|
||||
Model.Bikes.BikeInfoNS.BC.IBikeInfoMutable selectedBike,
|
||||
|
@ -171,7 +172,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
switch (selectedBluetoothLockBike.LockInfo.State)
|
||||
{
|
||||
case LockingState.Closed:
|
||||
// Ride was paused.
|
||||
// User wants to close lock.
|
||||
return new BookedClosed(
|
||||
selectedBluetoothLockBike,
|
||||
isConnectedDelegate,
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -80,10 +80,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User selected bike {id} but opening lock failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageOpeningLockErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -97,7 +96,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -105,7 +104,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
Log.ForContext<BookedClosed>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -81,10 +81,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected bike {id} but opening lock failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageOpeningLockErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -98,7 +97,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -106,7 +105,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
Log.ForContext<BookedOpen>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
|
@ -92,10 +92,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableClosed>().Information("User selected bike {id} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -103,13 +102,13 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
Log.ForContext<DisposableClosed>().Error("User selected bike {id} but booking failed. {@exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
|
||||
|
@ -118,7 +117,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
Log.ForContext<DisposableClosed>().Information("User booked and released bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -151,7 +150,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -170,8 +169,8 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
Log.ForContext<DisposableClosed>().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageReservationBikeErrorTooManyReservationsRentals, SelectedBike.GetFullDisplayName(), (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageHintTitle,
|
||||
string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.GetFullDisplayName(), (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|
@ -180,10 +179,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableClosed>().Information("User selected centered bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageReservingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -191,14 +189,14 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
Log.ForContext<DisposableClosed>().Error("User selected centered bike {bike} but reserving failed. {@exception}", SelectedBike, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageReservingBikeErrorGeneralTitle,
|
||||
AppResources.ErrorReservingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
|
@ -206,7 +204,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
Log.ForContext<DisposableClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery, SelectedBike?.BookingFinishedModel?.Co2Saving);
|
||||
|
@ -93,12 +93,12 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.ErrorSubmitFeedbackTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -115,7 +115,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
|
|
@ -62,8 +62,8 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.QuestionAnswerYes,
|
||||
AppResources.QuestionAnswerNo);
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (result == false)
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
@ -95,7 +95,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {Id} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -104,23 +104,22 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BikesViewModel>().Information("User selected reserved bike {Id} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {Id} but cancel reservation failed. {@Exception}.", SelectedBike.Id, exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -129,7 +128,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
Log.ForContext<BikesViewModel>().Information("User canceled reservation of bike {Id} successfully.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -159,7 +158,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
|
@ -177,10 +176,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {Id} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -188,14 +186,14 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
Log.ForContext<ReservedClosed>().Error("User selected requested bike {Id} but reserving failed. {@Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
exception.Message,
|
||||
string.Empty,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
@ -203,7 +201,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
|
||||
Log.ForContext<ReservedClosed>().Information("User booked and opened bike {bike} successfully.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Plugin.BLE.Abstractions.Contracts;
|
||||
|
@ -150,7 +151,12 @@ namespace TINK.ViewModel.Bikes
|
|||
|
||||
m_oViewUpdateManager = new IdlePollingUpdateTaskManager();
|
||||
|
||||
BikeCollection = new BikeCollectionMutable();
|
||||
BikeCollection = new BikeCollectionMutable(
|
||||
geolocation,
|
||||
lockService,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
() => m_oViewUpdateManager);
|
||||
|
||||
BikeCollection.CollectionChanged += OnDecoratedCollectionChanged;
|
||||
|
||||
|
@ -337,6 +343,63 @@ namespace TINK.ViewModel.Bikes
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary> Used to display active rental process.</summary>
|
||||
private IRentalProcess _rentalProcess = new RentalProcess();
|
||||
|
||||
/// <summary> Holds the active rental process.</summary>
|
||||
public IRentalProcess RentalProcess
|
||||
{
|
||||
get => _rentalProcess;
|
||||
set
|
||||
{
|
||||
if (value == _rentalProcess)
|
||||
return;
|
||||
|
||||
_rentalProcess = value;
|
||||
|
||||
BikeInRentalProcess = this.FirstOrDefault(bike => bike.Id == _rentalProcess.BikeId) as Bike.BluetoothLock.BikeViewModel;
|
||||
|
||||
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(RentalProcess)));
|
||||
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(BikeInRentalProcess)));
|
||||
}
|
||||
}
|
||||
|
||||
public Bike.BluetoothLock.BikeViewModel BikeInRentalProcess { get; private set; }
|
||||
|
||||
/// <summary> Used to display current step in rental process.</summary>
|
||||
private int? currentStep = null;
|
||||
|
||||
/// <summary> Holds the number of current step in rental process.</summary>
|
||||
public int? CurrentStep
|
||||
{
|
||||
get => currentStep;
|
||||
set
|
||||
{
|
||||
if (value == CurrentStep)
|
||||
return;
|
||||
|
||||
currentStep = value;
|
||||
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(CurrentStep)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Used to display status of current step in rental process.</summary>
|
||||
private CurrentStepStatus currentStepStatus = CurrentStepStatus.None;
|
||||
|
||||
/// <summary> Holds the status of current step in rental process.e</summary>
|
||||
public virtual CurrentStepStatus CurrentStepStatus
|
||||
{
|
||||
get => currentStepStatus;
|
||||
set
|
||||
{
|
||||
if (value == CurrentStepStatus)
|
||||
return;
|
||||
|
||||
currentStepStatus = value;
|
||||
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(CurrentStepStatus)));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProcessWithRunningProcessView => !isIdle;
|
||||
|
||||
/// <summary> Holds info about current action. </summary>
|
||||
|
@ -389,16 +452,11 @@ namespace TINK.ViewModel.Bikes
|
|||
{
|
||||
get
|
||||
{
|
||||
if (Exception != null)
|
||||
{
|
||||
// An error occurred getting data from copri.
|
||||
return Exception.GetShortErrorInfoText(IsReportLevelVerbose);
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
return AppResources.ActivityTextConnectionStateOffline;
|
||||
}
|
||||
//if (Exception != null)
|
||||
//{
|
||||
// // An error occurred getting data from copri.
|
||||
// return Exception.GetShortErrorInfoText(IsReportLevelVerbose);
|
||||
//}
|
||||
|
||||
return ActionText ?? string.Empty;
|
||||
}
|
||||
|
@ -471,7 +529,7 @@ namespace TINK.ViewModel.Bikes
|
|||
try
|
||||
{
|
||||
// Update bikes at station or my bikes depending on context.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(m_oPolling);
|
||||
await m_oViewUpdateManager.StartAsync(m_oPolling);
|
||||
}
|
||||
catch (Exception l_oExcetion)
|
||||
{
|
||||
|
@ -484,7 +542,7 @@ namespace TINK.ViewModel.Bikes
|
|||
/// Currently invoked by code behind, would be nice if called by XAML in future versions.
|
||||
/// </summary>
|
||||
public virtual async Task OnDisappearing()
|
||||
=> await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
=> await m_oViewUpdateManager.StopAsync();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
namespace TINK.ViewModel.Bikes
|
||||
|
||||
namespace TINK.ViewModel.Bikes
|
||||
{
|
||||
public interface IBikesViewModel
|
||||
{
|
||||
/// <summary> Holds info about active rental process. </summary>
|
||||
IRentalProcess RentalProcess { get; set; }
|
||||
|
||||
/// <summary> Holds info about current action. </summary>
|
||||
string ActionText { get; set; }
|
||||
|
||||
|
|
31
TINKLib/ViewModel/Bikes/IRentalProcess.cs
Normal file
31
TINKLib/ViewModel/Bikes/IRentalProcess.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using TINK.Model;
|
||||
|
||||
namespace TINK.ViewModel.Bikes
|
||||
{
|
||||
public interface IRentalProcess
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the id of the bike which is rental ends.
|
||||
/// </summary>
|
||||
string BikeId { get; }
|
||||
|
||||
CurrentRentalProcess State { get; set; }
|
||||
|
||||
/// <summary> Holds info about current step in rental process. </summary>
|
||||
int? StepIndex { get; set; }
|
||||
|
||||
/// <summary> Holds info about current step of rental process. </summary>
|
||||
string StepInfoText { get; set; }
|
||||
|
||||
/// <summary> Holds important info about current step of rental process. </summary>
|
||||
string ImportantStepInfoText { get; set; }
|
||||
|
||||
/// <summary> Holds info about status of current rental process. </summary>
|
||||
CurrentStepStatus Result { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the info returned from back end when rent is ended.
|
||||
/// </summary>
|
||||
BookingFinishedModel EndRentalInfo { get; set; }
|
||||
}
|
||||
}
|
133
TINKLib/ViewModel/Bikes/RentalProcess.cs
Normal file
133
TINKLib/ViewModel/Bikes/RentalProcess.cs
Normal file
|
@ -0,0 +1,133 @@
|
|||
|
||||
using System.ComponentModel;
|
||||
using TINK.Model;
|
||||
|
||||
namespace TINK.ViewModel.Bikes
|
||||
{
|
||||
public enum CurrentRentalProcess
|
||||
{
|
||||
None = 0,
|
||||
ReserveBike = 1,
|
||||
StartRental = 2,
|
||||
OpenLock = 3,
|
||||
CloseLock = 4,
|
||||
EndRental = 5,
|
||||
}
|
||||
|
||||
public enum CurrentStepStatus
|
||||
{
|
||||
None = 0,
|
||||
Succeeded = 1,
|
||||
Failed = 2,
|
||||
}
|
||||
|
||||
public class RentalProcess : IRentalProcess, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public RentalProcess(string bikeId = null) => BikeId = bikeId ?? string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the bike which is rental ends.
|
||||
/// </summary>
|
||||
public string BikeId { get; }
|
||||
|
||||
/// <summary> Holds info about active rental process. </summary>
|
||||
private CurrentRentalProcess _state = CurrentRentalProcess.None;
|
||||
|
||||
/// <summary> Holds info about active rental process. </summary>
|
||||
public CurrentRentalProcess State
|
||||
{
|
||||
get => _state;
|
||||
set
|
||||
{
|
||||
if (_state == value) { return; }
|
||||
|
||||
_state = value;
|
||||
|
||||
if (value == CurrentRentalProcess.None)
|
||||
{
|
||||
StepIndex = null;
|
||||
Result = CurrentStepStatus.None;
|
||||
}
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StepIndex)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary> Holds info about current step in rental process. </summary>
|
||||
private int? _stepIndex;
|
||||
|
||||
/// <summary> Holds info about current step in rental process. </summary>
|
||||
public int? StepIndex
|
||||
{
|
||||
get => _stepIndex;
|
||||
set
|
||||
{
|
||||
if (_stepIndex == value) { return; }
|
||||
|
||||
_stepIndex = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StepIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Holds info about current step. </summary>
|
||||
private string _stepInfoText;
|
||||
|
||||
/// <summary> Holds info about current step. </summary>
|
||||
public string StepInfoText
|
||||
{
|
||||
get => _stepInfoText;
|
||||
set
|
||||
{
|
||||
if (value == _stepInfoText)
|
||||
return;
|
||||
|
||||
_stepInfoText = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StepInfoText)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Holds important info about current step. </summary>
|
||||
private string _importantStepInfoText;
|
||||
|
||||
/// <summary> Holds important info about current step. </summary>
|
||||
public string ImportantStepInfoText
|
||||
{
|
||||
get => _importantStepInfoText;
|
||||
set
|
||||
{
|
||||
if (value == _importantStepInfoText)
|
||||
return;
|
||||
|
||||
_importantStepInfoText = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImportantStepInfoText)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Holds info about status of current rental process. </summary>
|
||||
private CurrentStepStatus _result = CurrentStepStatus.None;
|
||||
|
||||
/// <summary> Holds info about status of current rental process. </summary>
|
||||
public CurrentStepStatus Result
|
||||
{
|
||||
get => _result;
|
||||
set
|
||||
{
|
||||
if (_result == value) { return; }
|
||||
|
||||
_result = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the info returned from back end when rent is ended.
|
||||
/// </summary>
|
||||
public BookingFinishedModel EndRentalInfo { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -253,7 +253,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before getting bikes info.
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
|
||||
ActionText = AppResources.ActivityTextBikesAtStationGetBikes;
|
||||
|
||||
|
@ -284,7 +284,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
if (status != Status.Granted)
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
@ -310,7 +310,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
if (GeolocationService.IsGeolcationEnabled == false)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementLocationActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
@ -326,7 +326,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementBluetoothActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
|
|
@ -165,14 +165,14 @@ namespace TINK.ViewModel.Info
|
|||
{
|
||||
// Ask for permission to append diagnostics.
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.QuestionSupportmailTitle,
|
||||
AppResources.QuestionSupportmailAttachmentTitle,
|
||||
AppResources.QuestionSupportmailAttachment,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
var message = new EmailMessage
|
||||
{
|
||||
To = new List<string> { APPSUPPORTMAILADDRESS },
|
||||
Subject = string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName)
|
||||
Subject = SelectedStation?.Id != null ? string.Format(AppResources.SupportmailSubjectAppmailWithStation, AppFlavorName, SelectedStation?.Id) : string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName)
|
||||
};
|
||||
|
||||
// Send with attachment.
|
||||
|
@ -276,7 +276,7 @@ namespace TINK.ViewModel.Info
|
|||
|
||||
/// <summary> Text providing the id of the selected station.</summary>
|
||||
public string SelectedStationId
|
||||
=> SelectedStation?.Id;
|
||||
=> SelectedStation?.Id != null ? SelectedStation?.Id : string.Empty;
|
||||
|
||||
public string SelectedStationName
|
||||
=> SelectedStation?.StationName;
|
||||
|
|
|
@ -292,8 +292,8 @@ namespace TINK.ViewModel.Contact
|
|||
&& status != Status.Granted)
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageCenterMapLocationPermissionOpenDialog,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.ErrorMapCenterNoLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
|
@ -384,7 +384,7 @@ namespace TINK.ViewModel.Contact
|
|||
// COPRI reports an auth cookie error.
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageWaring,
|
||||
AppResources.MessageMapPageErrorAuthcookieUndefined,
|
||||
AppResources.ErrorMapPageAuthcookieUndefined,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsConnected = TinkApp.GetIsConnected();
|
||||
|
@ -419,9 +419,9 @@ namespace TINK.ViewModel.Contact
|
|||
IsProcessWithRunningProcessView = false;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
$"Beim Anzeigen der Fahrradstandorte- Seite ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
|
||||
"OK");
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsMapPageEnabled = true;
|
||||
}
|
||||
|
@ -483,10 +483,11 @@ namespace TINK.ViewModel.Contact
|
|||
ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<SelectStationPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
$"Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {exception.Message}",
|
||||
"OK");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -636,17 +637,12 @@ namespace TINK.ViewModel.Contact
|
|||
{
|
||||
get
|
||||
{
|
||||
if (Exception != null)
|
||||
{
|
||||
// An error occurred getting data from copri.
|
||||
return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
|
||||
//if (Exception != null)
|
||||
//{
|
||||
// // An error occurred getting data from copri.
|
||||
// return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
|
||||
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
return AppResources.ActivityTextConnectionStateOffline;
|
||||
}
|
||||
//}
|
||||
|
||||
return ActionText ?? string.Empty;
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ namespace TINK.ViewModel.FindBike
|
|||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// Stop polling before getting bikes info.
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(BikeIdUserInput) /* Find bike page flyout was taped */
|
||||
&& BikeCollection.Count > 0 /* Bike was successfully selected */)
|
||||
|
@ -238,10 +238,9 @@ namespace TINK.ViewModel.FindBike
|
|||
// Copri server is not reachable.
|
||||
Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed (Copri server not reachable).");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorSelectBikeTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
|
@ -249,7 +248,7 @@ namespace TINK.ViewModel.FindBike
|
|||
Log.ForContext<FindBikePageViewModel>().Error("Getting bikes failed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.ErrorSelectBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -271,8 +270,8 @@ namespace TINK.ViewModel.FindBike
|
|||
if (selectedBike == null)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageErrorSelectBikeNoBikeFound, BikeIdUserInput),
|
||||
AppResources.MessageHintTitle,
|
||||
string.Format(AppResources.ErrorSelectBikeNoBikeFound, BikeIdUserInput),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
ActionText = string.Empty;
|
||||
|
@ -311,7 +310,7 @@ namespace TINK.ViewModel.FindBike
|
|||
if (permissionResult != Status.Granted)
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
@ -335,7 +334,7 @@ namespace TINK.ViewModel.FindBike
|
|||
else
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
@ -361,7 +360,7 @@ namespace TINK.ViewModel.FindBike
|
|||
if (GeolocationService.IsGeolcationEnabled == false)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementLocationActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
@ -378,7 +377,7 @@ namespace TINK.ViewModel.FindBike
|
|||
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementBluetoothActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
@ -419,7 +418,7 @@ namespace TINK.ViewModel.FindBike
|
|||
catch (Exception exception)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorSelectBikeTitle,
|
||||
AppResources.ErrorSelectBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
@ -506,6 +505,5 @@ namespace TINK.ViewModel.FindBike
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Settings;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
|
@ -7,14 +7,14 @@ namespace TINK.ViewModel
|
|||
{
|
||||
/// <summary>
|
||||
/// Invoked when page is shown.
|
||||
/// Actuates and awaits the first update process and starts a task wich actuate the subseqent update tasks.
|
||||
/// Actuates and awaits the first update process and starts a task which actuate the subsequent update tasks.
|
||||
/// </summary>
|
||||
Task StartUpdateAyncPeridically(PollingParameters p_oPolling = null);
|
||||
Task StartAsync(PollingParameters p_oPolling = null);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when pages is closed/ hidden.
|
||||
/// Stops update process.
|
||||
/// </summary>
|
||||
Task StopUpdatePeridically();
|
||||
Task StopAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ namespace TINK.ViewModel
|
|||
/// <remarks> Object MapPage calls OnDisappearing for some reasons after first start after installation before OnAppearing. This requires a IdlePollingUpdateManager to exist.</remarks>
|
||||
public class IdlePollingUpdateTaskManager : IPollingUpdateTaskManager
|
||||
{
|
||||
public async Task StartUpdateAyncPeridically(PollingParameters p_oPeriode)
|
||||
public async Task StartAsync(PollingParameters p_oPeriode)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StopUpdatePeridically()
|
||||
public async Task StopAsync()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
|
|
@ -223,8 +223,8 @@ namespace TINK.ViewModel
|
|||
else
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageLoginRegisterNoNet,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
});
|
||||
|
@ -239,8 +239,8 @@ namespace TINK.ViewModel
|
|||
else
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageLoginRecoverPassword,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
});
|
||||
|
@ -290,7 +290,7 @@ namespace TINK.ViewModel
|
|||
Log.ForContext<LoginPageViewModel>().Error("Login failed (invalid. auth. response). {@l_oException}.", l_oException);
|
||||
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLoginErrorTitle,
|
||||
AppResources.ErrorLoginTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
@ -301,7 +301,7 @@ namespace TINK.ViewModel
|
|||
catch (UnsupportedCopriVersionDetectedException)
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLoginErrorTitle,
|
||||
AppResources.ErrorLoginTitle,
|
||||
string.Format(
|
||||
AppResources.MessageAppVersionIsOutdated,
|
||||
TinkApp.Flavor.GetDisplayName()),
|
||||
|
@ -319,8 +319,8 @@ namespace TINK.ViewModel
|
|||
Log.ForContext<LoginPageViewModel>().Information("Login failed (web communication exception). {@l_oException}.", l_oException);
|
||||
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLoginConnectionErrorTitle,
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (l_oException is UsernamePasswordInvalidException)
|
||||
|
@ -328,15 +328,15 @@ namespace TINK.ViewModel
|
|||
// Cookie is empty.
|
||||
Log.ForContext<LoginPageViewModel>().Error("Login failed (empty cookie). {@l_oException}.", l_oException);
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLoginErrorTitle,
|
||||
string.Format(AppResources.MessageLoginConnectionErrorMessage, l_oException.Message),
|
||||
AppResources.ErrorLoginTitle,
|
||||
string.Format(AppResources.ErrorLoginNoCookie, l_oException.Message),
|
||||
"OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<LoginPageViewModel>().Error("Login failed. {@l_oException}.", l_oException);
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLoginErrorTitle,
|
||||
AppResources.ErrorLoginTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
|
|
@ -377,7 +377,7 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
// Show COPRI message once.
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleInformation,
|
||||
AppResources.MessageInformationTitle,
|
||||
resultStationsAndBikes.GeneralData.MerchantMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}, null);
|
||||
|
@ -442,7 +442,7 @@ namespace TINK.ViewModel.Map
|
|||
try
|
||||
{
|
||||
// Update bikes at station or my bikes depending on context.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling);
|
||||
await m_oViewUpdateManager.StartAsync(Polling);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -463,9 +463,9 @@ namespace TINK.ViewModel.Map
|
|||
IsNavBarVisible = true;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
$"Beim Anzeigen der Fahrradstandorte- Seite ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
|
||||
"OK");
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsMapPageEnabled = true;
|
||||
}
|
||||
|
@ -512,7 +512,7 @@ namespace TINK.ViewModel.Map
|
|||
// COPRI reports an auth cookie error.
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageWaring,
|
||||
AppResources.MessageMapPageErrorAuthcookieUndefined,
|
||||
AppResources.ErrorMapPageAuthcookieUndefined,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsConnected = TinkApp.GetIsConnected();
|
||||
|
@ -607,8 +607,8 @@ namespace TINK.ViewModel.Map
|
|||
&& status != Status.Granted)
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageCenterMapLocationPermissionOpenDialog,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.ErrorMapCenterNoLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
|
@ -727,7 +727,7 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
Log.Information("Map page is disappearing...");
|
||||
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
}
|
||||
|
||||
/// <summary> User clicked on a bike. </summary>
|
||||
|
@ -764,9 +764,9 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
Log.ForContext<MapPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
$"Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {exception.Message}",
|
||||
"OK");
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
$"{AppResources.ErrorPageNotLoaded}\r\n {exception.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -931,15 +931,11 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
get
|
||||
{
|
||||
if (Exception != null)
|
||||
{
|
||||
// An error occurred getting data from copri.
|
||||
return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
|
||||
}
|
||||
if (!IsConnected)
|
||||
{
|
||||
return AppResources.ActivityTextConnectionStateOffline;
|
||||
}
|
||||
//if (Exception != null)
|
||||
//{
|
||||
// // An error occurred getting data from copri.
|
||||
// return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
|
||||
//}
|
||||
|
||||
return ActionText ?? string.Empty;
|
||||
}
|
||||
|
@ -968,9 +964,9 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
Log.ForContext<MapPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Meine Räder\" aufgetreten. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
$"Fehler beim Öffnen der Ansicht \"Meine Räder\" aufgetreten. {exception.Message}",
|
||||
"OK");
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1023,7 +1019,7 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
// Stop polling.
|
||||
ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
|
||||
// Clear error info.
|
||||
Exception = null;
|
||||
|
@ -1063,7 +1059,7 @@ namespace TINK.ViewModel.Map
|
|||
try
|
||||
{
|
||||
// Update bikes at station or my bikes depending on context.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling);
|
||||
await m_oViewUpdateManager.StartAsync(Polling);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -1084,9 +1080,9 @@ namespace TINK.ViewModel.Map
|
|||
IsNavBarVisible = true;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
AppResources.MessageMapPageErrorSwitch,
|
||||
String.Format(AppResources.MessageMapPageErrorSwitch, l_oException.Message),
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
AppResources.ErrorMapPageSwitchBikeType,
|
||||
String.Format(AppResources.ErrorMapPageSwitchBikeType, l_oException.Message),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsMapPageEnabled = true;
|
||||
|
|
|
@ -142,8 +142,8 @@ namespace TINK.ViewModel.MiniSurvey
|
|||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.ErrorSubmitFeedbackTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@ namespace TINK.ViewModel.MyBikes
|
|||
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
|
||||
|
||||
// Stop polling before getting bikes info.
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
|
||||
var bikesOccupied = await ConnectorFactory(IsConnected).Query.GetBikesOccupiedAsync();
|
||||
|
||||
|
@ -171,7 +171,7 @@ namespace TINK.ViewModel.MyBikes
|
|||
if (permissionResult != Status.Granted)
|
||||
{
|
||||
var dialogResult = await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
@ -197,7 +197,7 @@ namespace TINK.ViewModel.MyBikes
|
|||
if (GeolocationService.IsGeolcationEnabled == false)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementLocationActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
@ -214,7 +214,7 @@ namespace TINK.ViewModel.MyBikes
|
|||
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageHintTitle,
|
||||
AppResources.MessageBikesManagementBluetoothActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace TINK.ViewModel
|
|||
/// Actuates and awaits the first update process and starts a task wich actuate the subseqent update tasks.
|
||||
/// </summary>
|
||||
/// <param name="pollingParameters">Parametes holding polling period, if null last parameters are used if available.</param>
|
||||
public async Task StartUpdateAyncPeridically(PollingParameters pollingParameters = null)
|
||||
public async Task StartAsync(PollingParameters pollingParameters = null)
|
||||
{
|
||||
if (UpdateTask != null)
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ namespace TINK.ViewModel
|
|||
/// Invoked when pages is closed/ hidden.
|
||||
/// Stops update process.
|
||||
/// </summary>
|
||||
public async Task StopUpdatePeridically()
|
||||
public async Task StopAsync()
|
||||
{
|
||||
if (UpdateTask == null)
|
||||
{
|
||||
|
|
|
@ -319,16 +319,16 @@ namespace TINK.ViewModel
|
|||
|
||||
TinkApp.UpdateConnector();
|
||||
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
await m_oViewUpdateManager.StopAsync();
|
||||
|
||||
Log.ForContext<SettingsPageViewModel>().Information($"{nameof(OnDisappearing)} done.");
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
"Fehler",
|
||||
$"Ein unerwarteter Fehler ist aufgetreten. \r\n{l_oException.Message}",
|
||||
"OK");
|
||||
AppResources.ErrorPageNotLoadedTitle,
|
||||
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ namespace TINK.ViewModel
|
|||
|
||||
l_oError = new FormattedString();
|
||||
l_oError.Spans.Add(new Span { Text = "Information!\r\n", FontAttributes = FontAttributes.Bold });
|
||||
l_oError.Spans.Add(new Span { Text = $"{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" });
|
||||
l_oError.Spans.Add(new Span { Text = $"{exception.Message}\r\n{AppResources.ErrorNoWeb}" });
|
||||
return l_oError;
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue