mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-06-22 22:07:28 +02:00
Version 3.0.370
This commit is contained in:
parent
f5cf9bb22f
commit
bdb2dec1c1
233 changed files with 10252 additions and 6779 deletions
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue