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 { /// /// View model for action close bluetooth lock. /// /// internal class CloseLockActionViewModel : ICloseCommandListener, INotifyPropertyChanged { /// Notifies view about changes. public event PropertyChangedEventHandler PropertyChanged; /// /// View model to be used for progress report and unlocking/ locking view. /// private IBikesViewModel BikesViewModel { get; set; } /// /// View service to show modal notifications. /// private IViewService ViewService { get; } /// /// Service to control locks. /// private ILocksService LockService { get; } /// Provides a connector object. protected Func ConnectorFactory { get; } /// Delegate to retrieve connected state. private Func IsConnectedDelegate { get; } /// Gets the is connected state. bool IsConnected; /// Object to start or stop update of view model objects from Copri. private Func ViewUpdateManager { get; } /// Bike close. private Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable SelectedBike { get; } /// /// Constructs the object. /// /// Bike to close. /// Object to start or stop update of view model objects from Copri. /// View service to show modal notifications. /// View model to be used for progress report and unlocking/ locking view. /// public CloseLockActionViewModel( Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable selectedBike, Func 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."); } /// /// Processes the close lock progress. /// /// Current step to process. 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; } } /// /// Processes the close lock state. /// /// State to process. /// Textual details describing current state. 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; } } /// Close lock in order to pause ride and update COPRI lock state. public async Task CloseLockAsync() { Log.ForContext().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().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; } /// Opens support. //#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; // } // } /// /// True if user requested End rental. /// private bool isEndRentalRequested = false; /// /// True if user requested End rental. /// public bool IsEndRentalRequested { get { return isEndRentalRequested; } set { isEndRentalRequested = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsEndRentalRequested))); } } } }