Version 3.0.370

This commit is contained in:
Anja 2023-08-31 12:20:06 +02:00
parent f5cf9bb22f
commit bdb2dec1c1
233 changed files with 10252 additions and 6779 deletions

View file

@ -252,18 +252,18 @@ namespace TINK.ViewModel.Account
return string.Empty;
}
var bookingStateInfo = string.Format("Aktuell {0} Fahrräder reserviert/ gebucht.", m_iMyBikesCount.Value);
var bookingStateInfo = string.Format(AppResources.MarkingAccountReservedBookedBikes, m_iMyBikesCount.Value);
if (!IsConnected)
{
// Append offline info
return $"{bookingStateInfo} Verbindungsstatus: Offline.";
return $"{bookingStateInfo} {AppResources.MarkingAccountConnectionOffline}";
}
if (Exception != null)
{
// Append offline info
return $"{bookingStateInfo} Verbindungstatus: Verbindung unterbrochen. ";
return $"{bookingStateInfo} {AppResources.MarkingAccountConnectionInterrupted} ";
}
return bookingStateInfo;
@ -289,9 +289,9 @@ namespace TINK.ViewModel.Account
else
{
await m_oViewService.DisplayAlert(
"Hinweis",
"Bitte mit Internet verbinden zum Verwalten der persönlichen Daten.",
"OK");
AppResources.MessageHintTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
});
@ -306,10 +306,10 @@ namespace TINK.ViewModel.Account
try
{
// Backup logout message before logout.
var l_oMessage = string.Format("Benutzer {0} abgemeldet.", TinkApp.ActiveUser.Mail);
var l_oMessage = string.Format(AppResources.MessageLogoutGoodbye, TinkApp.ActiveUser.Mail);
// Stop polling before requesting bike.
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
try
{
@ -320,12 +320,12 @@ namespace TINK.ViewModel.Account
catch (UnsupportedCopriVersionDetectedException)
{
await m_oViewService.DisplayAlert(
AppResources.MessageLogoutErrorTitle,
AppResources.ErrorLogoutTitle,
string.Format(AppResources.MessageAppVersionIsOutdated, TinkApp.Flavor.GetDisplayName()),
AppResources.MessageAnswerOk);
// Restart polling again.
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
await m_oViewUpdateManager.StartAsync(Polling.ToImmutable());
IsIdle = true;
StatusInfoText = string.Empty;
@ -337,17 +337,20 @@ namespace TINK.ViewModel.Account
if (l_oException is WebConnectFailureException)
{
await m_oViewService.DisplayAlert(
"Verbingungsfehler bei Abmeldung!",
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
"OK");
AppResources.ErrorLogoutTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
{
await m_oViewService.DisplayAlert("Fehler bei Abmeldung!", l_oException.Message, "OK");
await m_oViewService.DisplayAlert(
AppResources.ErrorLogoutTitle,
l_oException.Message,
AppResources.MessageAnswerOk);
}
// Restart polling again.
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
await m_oViewUpdateManager.StartAsync(Polling.ToImmutable());
IsIdle = true;
StatusInfoText = string.Empty;
@ -356,8 +359,11 @@ namespace TINK.ViewModel.Account
TinkApp.ActiveUser.Logout();
// Display information that log out was perfomrmed.
await m_oViewService.DisplayAlert("Auf Wiedersehen!", l_oMessage, "OK");
// Display information that log out was performed.
await m_oViewService.DisplayAlert(
AppResources.MessageLogoutGoodbyeTitle,
l_oMessage,
AppResources.MessageAnswerOk);
}
catch (Exception p_oException)
{
@ -428,7 +434,7 @@ namespace TINK.ViewModel.Account
try
{
// Update bikes at station or my bikes depending on context.
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
await m_oViewUpdateManager.StartAsync(Polling.ToImmutable());
}
catch (Exception l_oExcetion)
{
@ -445,15 +451,15 @@ namespace TINK.ViewModel.Account
{
Log.ForContext<AccountPageViewModel>().Information($"Entering {nameof(OnDisappearing)}...");
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
}
catch (Exception l_oException)
{
await m_oViewService.DisplayAlert(
"Fehler",
$"Ein unerwarteter Fehler ist aufgetreten. \r\n{l_oException.Message}",
"OK");
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
AppResources.MessageAnswerOk);
}
}

View file

@ -55,7 +55,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
// Stop polling before requesting bike.
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
IsConnected = IsConnectedDelegate();
@ -72,8 +72,8 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
string.Format(AppResources.MessageReservationBikeErrorTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
AppResources.MessageHintTitle,
string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
AppResources.MessageAnswerOk);
}
else if (exception is WebConnectFailureException
@ -84,8 +84,8 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
AppResources.MessageReservingBikeErrorConnectionTitle,
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
@ -93,7 +93,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
Log.ForContext<Disposable>().Error("User selected centered bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, exception);
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(AppResources.MessageReservingBikeErrorGeneralTitle, exception.Message, AppResources.MessageAnswerOk);
await ViewService.DisplayAlert(AppResources.ErrorReservingBikeTitle, exception.Message, AppResources.MessageAnswerOk);
}
BikesViewModel.ActionText = string.Empty; // Todo: Remove this statement because in catch block ActionText is already set to empty above.
@ -104,7 +104,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
finally
{
// Restart polling again.
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
// Update status text and unlock list of bikes because no more action is pending.
BikesViewModel.ActionText = string.Empty; // Todo: Move this statement in front of finally block because in catch block ActionText is already set to empty.

View file

@ -53,7 +53,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
// Stop polling before cancel request.
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
try
{
@ -80,7 +80,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
BikesViewModel.ActionText = String.Empty;
await ViewService.DisplayAlert(
"Verbingungsfehler beim Stornieren der Buchung!",
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
AppResources.ErrorNoWeb,
"OK");
BikesViewModel.IsIdle = true;
return this;
@ -97,7 +97,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
finally
{
// Restart polling again.
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
// Unlock list of bikes because no more action is pending.
BikesViewModel.ActionText = string.Empty; // Todo: Move this statement in front of finally block because in catch block ActionText is already set to empty.

View file

@ -202,6 +202,8 @@ namespace TINK.ViewModel.Bikes.Bike
public string StationId => $"Station {Bike.StationId}";
public string DisplayName => Bike.GetDisplayName();
public bool IsBikeWithCopriLock => Bike.LockModel == Model.Bikes.BikeInfoNS.BikeNS.LockModel.Sigo;
/// Returns if type of bike is a cargo pedelec bike.

View file

@ -225,5 +225,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
}
public string ErrorText => RequestHandler.ErrorText;
}
}

View file

@ -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)));
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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; }

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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.

View file

@ -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)
{

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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,

View file

@ -63,7 +63,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Stop polling before returning bike.
BikesViewModel.IsIdle = false;
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
IsConnected = IsConnectedDelegate();
@ -80,10 +80,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Copri server is not reachable.
Log.ForContext<BookedClosed>().Information("User selected bike {id} but opening lock failed (Copri server not reachable).", SelectedBike.Id);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageOpeningLockErrorConnectionTitle,
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
exception.Message,
await ViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
@ -97,7 +96,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
@ -105,7 +104,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<BookedClosed>().Information("User paused ride using {bike} successfully.", SelectedBike);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);

View file

@ -64,7 +64,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Stop polling before returning bike.
BikesViewModel.IsIdle = false;
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
IsConnected = IsConnectedDelegate();
@ -81,10 +81,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Copri server is not reachable.
Log.ForContext<BookedOpen>().Information("User selected bike {id} but opening lock failed (Copri server not reachable).", SelectedBike.Id);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageOpeningLockErrorConnectionTitle,
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
exception.Message,
await ViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
@ -98,7 +97,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
@ -106,7 +105,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<BookedOpen>().Information("User paused ride using {bike} successfully.", SelectedBike);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);

View file

@ -74,7 +74,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Stop polling before returning bike.
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
// Book bike prior to opening lock.
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
@ -92,10 +92,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Copri server is not reachable.
Log.ForContext<DisposableClosed>().Information("User selected bike {id} but booking failed (Copri server not reachable).", SelectedBike.Id);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageRentingBikeErrorConnectionTitle,
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
exception.Message,
await ViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
@ -103,13 +102,13 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<DisposableClosed>().Error("User selected bike {id} but booking failed. {@exception}", SelectedBike.Id, exception);
await ViewService.DisplayAlert(
AppResources.MessageRentingBikeErrorGeneralTitle,
AppResources.ErrorRentingBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
@ -118,7 +117,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<DisposableClosed>().Information("User booked and released bike {bike} successfully.", SelectedBike);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
@ -151,7 +150,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
// Stop polling before requesting bike.
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
IsConnected = IsConnectedDelegate();
@ -170,8 +169,8 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<DisposableClosed>().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
string.Format(AppResources.MessageReservationBikeErrorTooManyReservationsRentals, SelectedBike.GetFullDisplayName(), (exception as BookingDeclinedException).MaxBikesCount),
AppResources.MessageHintTitle,
string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.GetFullDisplayName(), (exception as BookingDeclinedException).MaxBikesCount),
AppResources.MessageAnswerOk);
}
else if (exception is WebConnectFailureException
@ -180,10 +179,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Copri server is not reachable.
Log.ForContext<DisposableClosed>().Information("User selected centered bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageReservingBikeErrorConnectionTitle,
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
exception.Message,
await ViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
@ -191,14 +189,14 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<DisposableClosed>().Error("User selected centered bike {bike} but reserving failed. {@exception}", SelectedBike, exception);
await ViewService.DisplayAlert(
AppResources.MessageReservingBikeErrorGeneralTitle,
AppResources.ErrorReservingBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
}
// Restart polling again.
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return this;
@ -206,7 +204,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<DisposableClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);

View file

@ -59,7 +59,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
{
BikesViewModel.IsIdle = false;
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
// Do get Feedback
var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery, SelectedBike?.BookingFinishedModel?.Co2Saving);
@ -93,12 +93,12 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
}
await ViewService.DisplayAlert(
AppResources.ErrorReturnSubmitFeedbackTitle,
AppResources.ErrorReturnSubmitFeedbackMessage,
AppResources.ErrorSubmitFeedbackTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
@ -115,7 +115,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
await ViewUpdateManager().StartAsync();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);

View file

@ -62,8 +62,8 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
var result = await ViewService.DisplayAlert(
string.Empty,
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
AppResources.QuestionAnswerYes,
AppResources.QuestionAnswerNo);
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
if (result == false)
{
@ -77,7 +77,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Stop polling before cancel request.
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
IsConnected = IsConnectedDelegate();
@ -95,7 +95,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {Id} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
await ViewService.DisplayAlert(
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
AppResources.ErrorCancelReservationTitle,
exception.Message,
AppResources.MessageAnswerOk);
}
@ -104,23 +104,22 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
{
// Copri server is not reachable.
Log.ForContext<BikesViewModel>().Information("User selected reserved bike {Id} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
exception.Message,
await ViewService.DisplayAlert(
AppResources.ErrorCancelReservationTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
{
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {Id} but cancel reservation failed. {@Exception}.", SelectedBike.Id, exception);
await ViewService.DisplayAlert(
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
AppResources.ErrorCancelReservationTitle,
exception.Message,
AppResources.MessageAnswerOk);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
@ -129,7 +128,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<BikesViewModel>().Information("User canceled reservation of bike {Id} successfully.", SelectedBike.Id);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
@ -159,7 +158,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Stop polling before cancel request.
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
await ViewUpdateManager().StopAsync();
// Book bike prior to opening lock.
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
@ -177,10 +176,9 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Copri server is not reachable.
Log.ForContext<ReservedClosed>().Information("User selected requested bike {Id} but booking failed (Copri server not reachable).", SelectedBike.Id);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageRentingBikeErrorConnectionTitle,
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
exception.Message,
await ViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
@ -188,14 +186,14 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<ReservedClosed>().Error("User selected requested bike {Id} but reserving failed. {@Exception}", SelectedBike.Id, exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageRentingBikeErrorGeneralTitle,
AppResources.ErrorRentingBikeTitle,
exception.Message,
string.Empty,
AppResources.MessageAnswerOk);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
@ -203,7 +201,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
Log.ForContext<ReservedClosed>().Information("User booked and opened bike {bike} successfully.", SelectedBike.Id);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Plugin.BLE.Abstractions.Contracts;
@ -150,7 +151,12 @@ namespace TINK.ViewModel.Bikes
m_oViewUpdateManager = new IdlePollingUpdateTaskManager();
BikeCollection = new BikeCollectionMutable();
BikeCollection = new BikeCollectionMutable(
geolocation,
lockService,
isConnectedDelegate,
connectorFactory,
() => m_oViewUpdateManager);
BikeCollection.CollectionChanged += OnDecoratedCollectionChanged;
@ -337,6 +343,63 @@ namespace TINK.ViewModel.Bikes
}
}
/// <summary> Used to display active rental process.</summary>
private IRentalProcess _rentalProcess = new RentalProcess();
/// <summary> Holds the active rental process.</summary>
public IRentalProcess RentalProcess
{
get => _rentalProcess;
set
{
if (value == _rentalProcess)
return;
_rentalProcess = value;
BikeInRentalProcess = this.FirstOrDefault(bike => bike.Id == _rentalProcess.BikeId) as Bike.BluetoothLock.BikeViewModel;
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(RentalProcess)));
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(BikeInRentalProcess)));
}
}
public Bike.BluetoothLock.BikeViewModel BikeInRentalProcess { get; private set; }
/// <summary> Used to display current step in rental process.</summary>
private int? currentStep = null;
/// <summary> Holds the number of current step in rental process.</summary>
public int? CurrentStep
{
get => currentStep;
set
{
if (value == CurrentStep)
return;
currentStep = value;
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(CurrentStep)));
}
}
/// <summary> Used to display status of current step in rental process.</summary>
private CurrentStepStatus currentStepStatus = CurrentStepStatus.None;
/// <summary> Holds the status of current step in rental process.e</summary>
public virtual CurrentStepStatus CurrentStepStatus
{
get => currentStepStatus;
set
{
if (value == CurrentStepStatus)
return;
currentStepStatus = value;
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(CurrentStepStatus)));
}
}
public bool IsProcessWithRunningProcessView => !isIdle;
/// <summary> Holds info about current action. </summary>
@ -389,16 +452,11 @@ namespace TINK.ViewModel.Bikes
{
get
{
if (Exception != null)
{
// An error occurred getting data from copri.
return Exception.GetShortErrorInfoText(IsReportLevelVerbose);
}
if (!IsConnected)
{
return AppResources.ActivityTextConnectionStateOffline;
}
//if (Exception != null)
//{
// // An error occurred getting data from copri.
// return Exception.GetShortErrorInfoText(IsReportLevelVerbose);
//}
return ActionText ?? string.Empty;
}
@ -471,7 +529,7 @@ namespace TINK.ViewModel.Bikes
try
{
// Update bikes at station or my bikes depending on context.
await m_oViewUpdateManager.StartUpdateAyncPeridically(m_oPolling);
await m_oViewUpdateManager.StartAsync(m_oPolling);
}
catch (Exception l_oExcetion)
{
@ -484,7 +542,7 @@ namespace TINK.ViewModel.Bikes
/// Currently invoked by code behind, would be nice if called by XAML in future versions.
/// </summary>
public virtual async Task OnDisappearing()
=> await m_oViewUpdateManager.StopUpdatePeridically();
=> await m_oViewUpdateManager.StopAsync();
}
}

View file

@ -1,7 +1,11 @@
namespace TINK.ViewModel.Bikes
namespace TINK.ViewModel.Bikes
{
public interface IBikesViewModel
{
/// <summary> Holds info about active rental process. </summary>
IRentalProcess RentalProcess { get; set; }
/// <summary> Holds info about current action. </summary>
string ActionText { get; set; }

View file

@ -0,0 +1,31 @@
using TINK.Model;
namespace TINK.ViewModel.Bikes
{
public interface IRentalProcess
{
/// <summary>
/// Gets the id of the bike which is rental ends.
/// </summary>
string BikeId { get; }
CurrentRentalProcess State { get; set; }
/// <summary> Holds info about current step in rental process. </summary>
int? StepIndex { get; set; }
/// <summary> Holds info about current step of rental process. </summary>
string StepInfoText { get; set; }
/// <summary> Holds important info about current step of rental process. </summary>
string ImportantStepInfoText { get; set; }
/// <summary> Holds info about status of current rental process. </summary>
CurrentStepStatus Result { get; set; }
/// <summary>
/// Holds the info returned from back end when rent is ended.
/// </summary>
BookingFinishedModel EndRentalInfo { get; set; }
}
}

View file

@ -0,0 +1,133 @@
using System.ComponentModel;
using TINK.Model;
namespace TINK.ViewModel.Bikes
{
public enum CurrentRentalProcess
{
None = 0,
ReserveBike = 1,
StartRental = 2,
OpenLock = 3,
CloseLock = 4,
EndRental = 5,
}
public enum CurrentStepStatus
{
None = 0,
Succeeded = 1,
Failed = 2,
}
public class RentalProcess : IRentalProcess, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public RentalProcess(string bikeId = null) => BikeId = bikeId ?? string.Empty;
/// <summary>
/// Gets the id of the bike which is rental ends.
/// </summary>
public string BikeId { get; }
/// <summary> Holds info about active rental process. </summary>
private CurrentRentalProcess _state = CurrentRentalProcess.None;
/// <summary> Holds info about active rental process. </summary>
public CurrentRentalProcess State
{
get => _state;
set
{
if (_state == value) { return; }
_state = value;
if (value == CurrentRentalProcess.None)
{
StepIndex = null;
Result = CurrentStepStatus.None;
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StepIndex)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result)));
}
}
/// <summary> Holds info about current step in rental process. </summary>
private int? _stepIndex;
/// <summary> Holds info about current step in rental process. </summary>
public int? StepIndex
{
get => _stepIndex;
set
{
if (_stepIndex == value) { return; }
_stepIndex = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StepIndex)));
}
}
/// <summary> Holds info about current step. </summary>
private string _stepInfoText;
/// <summary> Holds info about current step. </summary>
public string StepInfoText
{
get => _stepInfoText;
set
{
if (value == _stepInfoText)
return;
_stepInfoText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StepInfoText)));
}
}
/// <summary> Holds important info about current step. </summary>
private string _importantStepInfoText;
/// <summary> Holds important info about current step. </summary>
public string ImportantStepInfoText
{
get => _importantStepInfoText;
set
{
if (value == _importantStepInfoText)
return;
_importantStepInfoText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImportantStepInfoText)));
}
}
/// <summary> Holds info about status of current rental process. </summary>
private CurrentStepStatus _result = CurrentStepStatus.None;
/// <summary> Holds info about status of current rental process. </summary>
public CurrentStepStatus Result
{
get => _result;
set
{
if (_result == value) { return; }
_result = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result)));
}
}
/// <summary>
/// Holds the info returned from back end when rent is ended.
/// </summary>
public BookingFinishedModel EndRentalInfo { get; set; }
}
}

View file

@ -253,7 +253,7 @@ namespace TINK.ViewModel.BikesAtStation
ActionText = AppResources.ActivityTextOneMomentPlease;
// Stop polling before getting bikes info.
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
ActionText = AppResources.ActivityTextBikesAtStationGetBikes;
@ -284,7 +284,7 @@ namespace TINK.ViewModel.BikesAtStation
if (status != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -310,7 +310,7 @@ namespace TINK.ViewModel.BikesAtStation
if (GeolocationService.IsGeolcationEnabled == false)
{
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationActivation,
AppResources.MessageAnswerOk);
@ -326,7 +326,7 @@ namespace TINK.ViewModel.BikesAtStation
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
{
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementBluetoothActivation,
AppResources.MessageAnswerOk);

View file

@ -165,14 +165,14 @@ namespace TINK.ViewModel.Info
{
// Ask for permission to append diagnostics.
await ViewService.DisplayAlert(
AppResources.QuestionSupportmailTitle,
AppResources.QuestionSupportmailAttachmentTitle,
AppResources.QuestionSupportmailAttachment,
AppResources.MessageAnswerOk);
var message = new EmailMessage
{
To = new List<string> { APPSUPPORTMAILADDRESS },
Subject = string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName)
Subject = SelectedStation?.Id != null ? string.Format(AppResources.SupportmailSubjectAppmailWithStation, AppFlavorName, SelectedStation?.Id) : string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName)
};
// Send with attachment.
@ -276,7 +276,7 @@ namespace TINK.ViewModel.Info
/// <summary> Text providing the id of the selected station.</summary>
public string SelectedStationId
=> SelectedStation?.Id;
=> SelectedStation?.Id != null ? SelectedStation?.Id : string.Empty;
public string SelectedStationName
=> SelectedStation?.StationName;

View file

@ -292,8 +292,8 @@ namespace TINK.ViewModel.Contact
&& status != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageCenterMapLocationPermissionOpenDialog,
AppResources.MessageHintTitle,
AppResources.ErrorMapCenterNoLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -384,7 +384,7 @@ namespace TINK.ViewModel.Contact
// COPRI reports an auth cookie error.
await ViewService.DisplayAlert(
AppResources.MessageWaring,
AppResources.MessageMapPageErrorAuthcookieUndefined,
AppResources.ErrorMapPageAuthcookieUndefined,
AppResources.MessageAnswerOk);
IsConnected = TinkApp.GetIsConnected();
@ -419,9 +419,9 @@ namespace TINK.ViewModel.Contact
IsProcessWithRunningProcessView = false;
await ViewService.DisplayAlert(
"Fehler",
$"Beim Anzeigen der Fahrradstandorte- Seite ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
"OK");
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
AppResources.MessageAnswerOk);
IsMapPageEnabled = true;
}
@ -483,10 +483,11 @@ namespace TINK.ViewModel.Contact
ActionText = string.Empty;
Log.ForContext<SelectStationPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {Exception}", exception);
await ViewService.DisplayAlert(
"Fehler",
$"Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {exception.Message}",
"OK");
await ViewService.DisplayAlert(
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}",
AppResources.MessageAnswerOk);
}
#endif
}
@ -636,17 +637,12 @@ namespace TINK.ViewModel.Contact
{
get
{
if (Exception != null)
{
// An error occurred getting data from copri.
return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
//if (Exception != null)
//{
// // An error occurred getting data from copri.
// return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
}
if (!IsConnected)
{
return AppResources.ActivityTextConnectionStateOffline;
}
//}
return ActionText ?? string.Empty;
}

View file

@ -175,7 +175,7 @@ namespace TINK.ViewModel.FindBike
IsConnected = IsConnectedDelegate();
// Stop polling before getting bikes info.
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
if (string.IsNullOrEmpty(BikeIdUserInput) /* Find bike page flyout was taped */
&& BikeCollection.Count > 0 /* Bike was successfully selected */)
@ -238,10 +238,9 @@ namespace TINK.ViewModel.FindBike
// Copri server is not reachable.
Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed (Copri server not reachable).");
await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle,
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
exception.Message,
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
@ -249,7 +248,7 @@ namespace TINK.ViewModel.FindBike
Log.ForContext<FindBikePageViewModel>().Error("Getting bikes failed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.ErrorSelectBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
}
@ -271,8 +270,8 @@ namespace TINK.ViewModel.FindBike
if (selectedBike == null)
{
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
string.Format(AppResources.MessageErrorSelectBikeNoBikeFound, BikeIdUserInput),
AppResources.MessageHintTitle,
string.Format(AppResources.ErrorSelectBikeNoBikeFound, BikeIdUserInput),
AppResources.MessageAnswerOk);
ActionText = string.Empty;
@ -311,7 +310,7 @@ namespace TINK.ViewModel.FindBike
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -335,7 +334,7 @@ namespace TINK.ViewModel.FindBike
else
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -361,7 +360,7 @@ namespace TINK.ViewModel.FindBike
if (GeolocationService.IsGeolcationEnabled == false)
{
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationActivation,
AppResources.MessageAnswerOk);
@ -378,7 +377,7 @@ namespace TINK.ViewModel.FindBike
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
{
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementBluetoothActivation,
AppResources.MessageAnswerOk);
@ -419,7 +418,7 @@ namespace TINK.ViewModel.FindBike
catch (Exception exception)
{
await ViewService.DisplayAlert(
AppResources.MessageErrorSelectBikeTitle,
AppResources.ErrorSelectBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
@ -506,6 +505,5 @@ namespace TINK.ViewModel.FindBike
}
}
}
}
}

View file

@ -1,4 +1,4 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using TINK.Settings;
namespace TINK.ViewModel
@ -7,14 +7,14 @@ namespace TINK.ViewModel
{
/// <summary>
/// Invoked when page is shown.
/// Actuates and awaits the first update process and starts a task wich actuate the subseqent update tasks.
/// Actuates and awaits the first update process and starts a task which actuate the subsequent update tasks.
/// </summary>
Task StartUpdateAyncPeridically(PollingParameters p_oPolling = null);
Task StartAsync(PollingParameters p_oPolling = null);
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
Task StopUpdatePeridically();
Task StopAsync();
}
}

View file

@ -7,12 +7,12 @@ namespace TINK.ViewModel
/// <remarks> Object MapPage calls OnDisappearing for some reasons after first start after installation before OnAppearing. This requires a IdlePollingUpdateManager to exist.</remarks>
public class IdlePollingUpdateTaskManager : IPollingUpdateTaskManager
{
public async Task StartUpdateAyncPeridically(PollingParameters p_oPeriode)
public async Task StartAsync(PollingParameters p_oPeriode)
{
await Task.CompletedTask;
}
public async Task StopUpdatePeridically()
public async Task StopAsync()
{
await Task.CompletedTask;
}

View file

@ -223,8 +223,8 @@ namespace TINK.ViewModel
else
{
await m_oViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageLoginRegisterNoNet,
AppResources.MessageHintTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
});
@ -239,8 +239,8 @@ namespace TINK.ViewModel
else
{
await m_oViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageLoginRecoverPassword,
AppResources.MessageHintTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
});
@ -290,7 +290,7 @@ namespace TINK.ViewModel
Log.ForContext<LoginPageViewModel>().Error("Login failed (invalid. auth. response). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.MessageLoginErrorTitle,
AppResources.ErrorLoginTitle,
l_oException.Message,
AppResources.MessageAnswerOk);
@ -301,7 +301,7 @@ namespace TINK.ViewModel
catch (UnsupportedCopriVersionDetectedException)
{
await m_oViewService.DisplayAlert(
AppResources.MessageLoginErrorTitle,
AppResources.ErrorLoginTitle,
string.Format(
AppResources.MessageAppVersionIsOutdated,
TinkApp.Flavor.GetDisplayName()),
@ -319,8 +319,8 @@ namespace TINK.ViewModel
Log.ForContext<LoginPageViewModel>().Information("Login failed (web communication exception). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.MessageLoginConnectionErrorTitle,
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else if (l_oException is UsernamePasswordInvalidException)
@ -328,15 +328,15 @@ namespace TINK.ViewModel
// Cookie is empty.
Log.ForContext<LoginPageViewModel>().Error("Login failed (empty cookie). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.MessageLoginErrorTitle,
string.Format(AppResources.MessageLoginConnectionErrorMessage, l_oException.Message),
AppResources.ErrorLoginTitle,
string.Format(AppResources.ErrorLoginNoCookie, l_oException.Message),
"OK");
}
else
{
Log.ForContext<LoginPageViewModel>().Error("Login failed. {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.MessageLoginErrorTitle,
AppResources.ErrorLoginTitle,
l_oException.Message,
AppResources.MessageAnswerOk);
}

View file

@ -377,7 +377,7 @@ namespace TINK.ViewModel.Map
{
// Show COPRI message once.
await ViewService.DisplayAlert(
AppResources.MessageTitleInformation,
AppResources.MessageInformationTitle,
resultStationsAndBikes.GeneralData.MerchantMessage,
AppResources.MessageAnswerOk);
}, null);
@ -442,7 +442,7 @@ namespace TINK.ViewModel.Map
try
{
// Update bikes at station or my bikes depending on context.
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling);
await m_oViewUpdateManager.StartAsync(Polling);
}
catch (Exception)
{
@ -463,9 +463,9 @@ namespace TINK.ViewModel.Map
IsNavBarVisible = true;
await ViewService.DisplayAlert(
"Fehler",
$"Beim Anzeigen der Fahrradstandorte- Seite ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
"OK");
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
AppResources.MessageAnswerOk);
IsMapPageEnabled = true;
}
@ -512,7 +512,7 @@ namespace TINK.ViewModel.Map
// COPRI reports an auth cookie error.
await ViewService.DisplayAlert(
AppResources.MessageWaring,
AppResources.MessageMapPageErrorAuthcookieUndefined,
AppResources.ErrorMapPageAuthcookieUndefined,
AppResources.MessageAnswerOk);
IsConnected = TinkApp.GetIsConnected();
@ -607,8 +607,8 @@ namespace TINK.ViewModel.Map
&& status != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageCenterMapLocationPermissionOpenDialog,
AppResources.MessageHintTitle,
AppResources.ErrorMapCenterNoLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -727,7 +727,7 @@ namespace TINK.ViewModel.Map
{
Log.Information("Map page is disappearing...");
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
}
/// <summary> User clicked on a bike. </summary>
@ -764,9 +764,9 @@ namespace TINK.ViewModel.Map
Log.ForContext<MapPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {Exception}", exception);
await ViewService.DisplayAlert(
"Fehler",
$"Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {exception.Message}",
"OK");
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n {exception.Message}",
AppResources.MessageAnswerOk);
}
#endif
}
@ -931,15 +931,11 @@ namespace TINK.ViewModel.Map
{
get
{
if (Exception != null)
{
// An error occurred getting data from copri.
return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
}
if (!IsConnected)
{
return AppResources.ActivityTextConnectionStateOffline;
}
//if (Exception != null)
//{
// // An error occurred getting data from copri.
// return Exception.GetShortErrorInfoText(TinkApp.IsReportLevelVerbose);
//}
return ActionText ?? string.Empty;
}
@ -968,9 +964,9 @@ namespace TINK.ViewModel.Map
Log.ForContext<MapPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Meine Räder\" aufgetreten. {Exception}", exception);
await ViewService.DisplayAlert(
"Fehler",
$"Fehler beim Öffnen der Ansicht \"Meine Räder\" aufgetreten. {exception.Message}",
"OK");
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}",
AppResources.MessageAnswerOk);
}
});
@ -1023,7 +1019,7 @@ namespace TINK.ViewModel.Map
// Stop polling.
ActionText = AppResources.ActivityTextOneMomentPlease;
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
// Clear error info.
Exception = null;
@ -1063,7 +1059,7 @@ namespace TINK.ViewModel.Map
try
{
// Update bikes at station or my bikes depending on context.
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling);
await m_oViewUpdateManager.StartAsync(Polling);
}
catch (Exception)
{
@ -1084,9 +1080,9 @@ namespace TINK.ViewModel.Map
IsNavBarVisible = true;
await ViewService.DisplayAlert(
"Fehler",
AppResources.MessageMapPageErrorSwitch,
String.Format(AppResources.MessageMapPageErrorSwitch, l_oException.Message),
AppResources.ErrorPageNotLoadedTitle,
AppResources.ErrorMapPageSwitchBikeType,
String.Format(AppResources.ErrorMapPageSwitchBikeType, l_oException.Message),
AppResources.MessageAnswerOk);
IsMapPageEnabled = true;

View file

@ -142,8 +142,8 @@ namespace TINK.ViewModel.MiniSurvey
}
await ViewService.DisplayAlert(
AppResources.ErrorReturnSubmitFeedbackTitle,
AppResources.ErrorReturnSubmitFeedbackMessage,
AppResources.ErrorSubmitFeedbackTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}

View file

@ -139,7 +139,7 @@ namespace TINK.ViewModel.MyBikes
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
// Stop polling before getting bikes info.
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
var bikesOccupied = await ConnectorFactory(IsConnected).Query.GetBikesOccupiedAsync();
@ -171,7 +171,7 @@ namespace TINK.ViewModel.MyBikes
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -197,7 +197,7 @@ namespace TINK.ViewModel.MyBikes
if (GeolocationService.IsGeolcationEnabled == false)
{
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationActivation,
AppResources.MessageAnswerOk);
@ -214,7 +214,7 @@ namespace TINK.ViewModel.MyBikes
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
{
await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementBluetoothActivation,
AppResources.MessageAnswerOk);

View file

@ -40,7 +40,7 @@ namespace TINK.ViewModel
/// Actuates and awaits the first update process and starts a task wich actuate the subseqent update tasks.
/// </summary>
/// <param name="pollingParameters">Parametes holding polling period, if null last parameters are used if available.</param>
public async Task StartUpdateAyncPeridically(PollingParameters pollingParameters = null)
public async Task StartAsync(PollingParameters pollingParameters = null)
{
if (UpdateTask != null)
{
@ -81,7 +81,7 @@ namespace TINK.ViewModel
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
public async Task StopUpdatePeridically()
public async Task StopAsync()
{
if (UpdateTask == null)
{

View file

@ -319,16 +319,16 @@ namespace TINK.ViewModel
TinkApp.UpdateConnector();
await m_oViewUpdateManager.StopUpdatePeridically();
await m_oViewUpdateManager.StopAsync();
Log.ForContext<SettingsPageViewModel>().Information($"{nameof(OnDisappearing)} done.");
}
catch (Exception l_oException)
{
await m_oViewService.DisplayAlert(
"Fehler",
$"Ein unerwarteter Fehler ist aufgetreten. \r\n{l_oException.Message}",
"OK");
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n{l_oException.Message}",
AppResources.MessageAnswerOk);
}
}

View file

@ -204,7 +204,7 @@ namespace TINK.ViewModel
l_oError = new FormattedString();
l_oError.Spans.Add(new Span { Text = "Information!\r\n", FontAttributes = FontAttributes.Bold });
l_oError.Spans.Add(new Span { Text = $"{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" });
l_oError.Spans.Add(new Span { Text = $"{exception.Message}\r\n{AppResources.ErrorNoWeb}" });
return l_oError;
}