mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-11-06 11:06:26 +01:00
415 lines
14 KiB
C#
415 lines
14 KiB
C#
|
using System;
|
||
|
using System.Threading.Tasks;
|
||
|
using Serilog;
|
||
|
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command;
|
||
|
using ShareeBike.MultilingualResources;
|
||
|
using ShareeBike.Services.Logging;
|
||
|
using ShareeBike.View;
|
||
|
using CloseCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
|
||
|
using CancelReservationCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.CancelReservationCommand;
|
||
|
using DisconnectCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.DisconnectCommand;
|
||
|
using System.ComponentModel;
|
||
|
|
||
|
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// View model for action close bluetooth lock.
|
||
|
/// </summary>
|
||
|
/// <typeparam name="T"></typeparam>
|
||
|
internal class CloseLockActionViewModel<T> : CloseCommand.ICloseCommandListener, CancelReservationCommand.ICancelReservationCommandListener, DisconnectCommand.IDisconnectCommandListener, 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>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(CloseCommand.Step step)
|
||
|
{
|
||
|
switch (step)
|
||
|
{
|
||
|
case CloseCommand.Step.StartStopingPolling:
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.Step.StartingQueryingLocation:
|
||
|
// 1a.Step: Start query geolocation data.
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.Step.ClosingLock:
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.Step.WaitStopPollingQueryLocation:
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.Step.QueryLocationTerminated:
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.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(CloseCommand.State state, string details)
|
||
|
{
|
||
|
switch (state)
|
||
|
{
|
||
|
case CloseCommand.State.OutOfReachError:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAlert(
|
||
|
AppResources.ErrorCloseLockTitle,
|
||
|
AppResources.ErrorLockOutOfReach,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.State.CouldntCloseMovingError:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAlert(
|
||
|
AppResources.ErrorCloseLockTitle,
|
||
|
AppResources.ErrorLockMoving,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.State.CouldntCloseBoltBlockedError:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAlert(
|
||
|
AppResources.ErrorCloseLockTitle,
|
||
|
AppResources.ErrorCloseLockBoltBlocked,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.State.GeneralCloseError:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAlert(
|
||
|
AppResources.ErrorCloseLockTitle,
|
||
|
details,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.State.WebConnectFailed:
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.State.ResponseIsInvalid:
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||
|
break;
|
||
|
|
||
|
case CloseCommand.State.BackendUpdateFailed:
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Processes the start reservation progress.
|
||
|
/// </summary>
|
||
|
/// <param name="step">Current step to process.</param>
|
||
|
public void ReportStep(CancelReservationCommand.Step step)
|
||
|
{
|
||
|
switch (step)
|
||
|
{
|
||
|
case CancelReservationCommand.Step.CancelReservation:
|
||
|
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessCancelReservation;
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Processes the start reservation state.
|
||
|
/// </summary>
|
||
|
/// <param name="state">State to process.</param>
|
||
|
/// <param name="details">Textual details describing current state.</param>
|
||
|
public async Task ReportStateAsync(CancelReservationCommand.State state, string details)
|
||
|
{
|
||
|
switch (state)
|
||
|
{
|
||
|
case CancelReservationCommand.State.InvalidResponse:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAlert(
|
||
|
AppResources.ErrorCancelReservationTitle,
|
||
|
AppResources.ErrorAccountInvalidAuthorization,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
break;
|
||
|
|
||
|
case CancelReservationCommand.State.WebConnectFailed:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAlert(
|
||
|
AppResources.ErrorNoConnectionTitle,
|
||
|
AppResources.ErrorNoWeb,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
break;
|
||
|
|
||
|
case CancelReservationCommand.State.GeneralCancelReservationError:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAdvancedAlert(
|
||
|
AppResources.ErrorCancelReservationTitle,
|
||
|
details,
|
||
|
AppResources.ErrorTryAgain,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Processes the disconnect from lock progress.
|
||
|
/// </summary>
|
||
|
/// <param name="step">Current step to process.</param>
|
||
|
public void ReportStep(DisconnectCommand.Step step)
|
||
|
{
|
||
|
switch (step)
|
||
|
{
|
||
|
case DisconnectCommand.Step.DisconnectLock:
|
||
|
BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessCloseLockStepUpload;
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Processes the disconnect from lock state.
|
||
|
/// </summary>
|
||
|
/// <param name="state">State to process.</param>
|
||
|
/// <param name="details">Textual details describing current state.</param>
|
||
|
public async Task ReportStateAsync(DisconnectCommand.State state, string details)
|
||
|
{
|
||
|
switch (state)
|
||
|
{
|
||
|
|
||
|
case DisconnectCommand.State.GeneralDisconnectError:
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
await ViewService.DisplayAdvancedAlert(
|
||
|
AppResources.ErrorAccountInvalidAuthorization,
|
||
|
details,
|
||
|
AppResources.ErrorTryAgain,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
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 close lock of bike {bikeId}.", SelectedBike.Id);
|
||
|
|
||
|
// 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
|
||
|
switch(SelectedBike.State.Value)
|
||
|
{
|
||
|
case Model.State.InUseStateEnum.Reserved:
|
||
|
BikesViewModel.StartRentalProcess(new RentalProcessViewModel(SelectedBike.Id)
|
||
|
{
|
||
|
State = CurrentRentalProcess.CloseLockAndCancelReservation,
|
||
|
StepIndex = 1,
|
||
|
Result = CurrentStepStatus.None
|
||
|
});
|
||
|
break;
|
||
|
|
||
|
case Model.State.InUseStateEnum.Disposable:
|
||
|
BikesViewModel.StartRentalProcess(new RentalProcessViewModel(SelectedBike.Id)
|
||
|
{
|
||
|
State = CurrentRentalProcess.CloseDisposableLock,
|
||
|
StepIndex = 1,
|
||
|
Result = CurrentStepStatus.None
|
||
|
});
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
BikesViewModel.StartRentalProcess(new RentalProcessViewModel(SelectedBike.Id)
|
||
|
{
|
||
|
State = CurrentRentalProcess.CloseLock,
|
||
|
StepIndex = 1,
|
||
|
Result = CurrentStepStatus.None
|
||
|
});
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
// 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("Lock of bike {bikeId} closed successfully.", SelectedBike.Id);
|
||
|
}
|
||
|
catch (Exception exception)
|
||
|
{
|
||
|
Log.ForContext<T>().Information("Lock of bike {bikeId} can not be closed. {@exception}", SelectedBike.Id, exception);
|
||
|
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||
|
await ViewUpdateManager().StartAsync();
|
||
|
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||
|
BikesViewModel.IsIdle = true;
|
||
|
return;
|
||
|
}
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||
|
|
||
|
// 2. Step
|
||
|
BikesViewModel.RentalProcess.StepIndex = 2;
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.None;
|
||
|
BikesViewModel.RentalProcess.ImportantStepInfoText = String.Empty;
|
||
|
|
||
|
|
||
|
if (SelectedBike.State.Value == Model.State.InUseStateEnum.Reserved)
|
||
|
{
|
||
|
// Cancel reservation
|
||
|
try
|
||
|
{
|
||
|
#if USELOCALINSTANCE
|
||
|
var command = new CancelReservationCommand(SelectedBike, ConnectorFactory, ViewUpdateManager);
|
||
|
await command.Invoke(this);
|
||
|
#else
|
||
|
await SelectedBike.CancelReservationAsync(this);
|
||
|
#endif
|
||
|
}
|
||
|
catch (Exception exception)
|
||
|
{
|
||
|
Log.ForContext<T>().Information("Reservation of bike {bikeId} could not be canceled. {@exception}", SelectedBike.Id, exception);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If bike is not reserved/booked, disconnect lock
|
||
|
if (SelectedBike.State.Value == Model.State.InUseStateEnum.Disposable)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
#if USELOCALINSTANCE
|
||
|
var command = new DisconnectCommand(SelectedBike, ConnectorFactory, ViewUpdateManager);
|
||
|
await command.Invoke(this);
|
||
|
#else
|
||
|
await SelectedBike.DisconnectAsync(this);
|
||
|
#endif
|
||
|
}
|
||
|
catch (Exception exception)
|
||
|
{
|
||
|
Log.ForContext<T>().Information("Lock of bike {bikeId} could not be disconnected. {@exception}", SelectedBike.Id, exception);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Question if park bike or end rental
|
||
|
IsEndRentalRequested = await ViewService.DisplayAlert(
|
||
|
AppResources.QuestionRentalProcessCloseLockEndOrContinueTitle,
|
||
|
AppResources.QuestionRentalProcessCloseLockEndOrContinueText,
|
||
|
AppResources.ActionEndRental,
|
||
|
AppResources.QuestionRentalProcessCloseLockContinueRentalAnswer);
|
||
|
|
||
|
BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded;
|
||
|
|
||
|
// Message for parking bike
|
||
|
if (IsEndRentalRequested == false)
|
||
|
{
|
||
|
Log.ForContext<T>().Information("User request to park bike {bikeId}.", SelectedBike.Id);
|
||
|
await ViewService.DisplayAlert(
|
||
|
AppResources.MessageRentalProcessCloseLockFinishedTitle,
|
||
|
AppResources.MessageRentalProcessCloseLockFinishedText,
|
||
|
AppResources.MessageAnswerOk);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||
|
await ViewUpdateManager().StartAsync();
|
||
|
BikesViewModel.ActionText = string.Empty;
|
||
|
|
||
|
BikesViewModel.RentalProcess.State = CurrentRentalProcess.None;
|
||
|
BikesViewModel.IsIdle = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Default value of user request to end rental = false.
|
||
|
/// </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)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|