mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-04-22 04:46:30 +02:00
Version 3.0.375
This commit is contained in:
parent
2c790239cb
commit
ca080c87c0
194 changed files with 10092 additions and 10464 deletions
|
@ -377,11 +377,7 @@ namespace TINK.ViewModel.Account
|
|||
try
|
||||
{
|
||||
// Switch to map view after log out.
|
||||
#if USEFLYOUT
|
||||
m_oViewService.ShowPage(ViewTypes.MapPage);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
/// <summary>
|
||||
/// Gets the name of the button when bike is cancel reservation.
|
||||
/// </summary>
|
||||
public string ButtonText => AppResources.ActionReturn; // "Miete beenden"
|
||||
public string ButtonText => AppResources.ActionEndRental; // "Miete beenden"
|
||||
|
||||
/// <summary>
|
||||
/// Reference on view service to show modal notifications and to perform navigation.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.State;
|
||||
|
@ -66,11 +66,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
try
|
||||
{
|
||||
// Switch to login page
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
#endif
|
||||
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(selectedBike, AppResources.ActionCancelRequest, true, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
|
||||
IUser activeUser) : base(selectedBike, AppResources.ActionCancelReservation, true, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ using System.ComponentModel;
|
|||
using System.Text.RegularExpressions;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
|
||||
using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS;
|
||||
#if !USEFLYOUT
|
||||
#endif
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
|
|
|
@ -153,7 +153,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
/// <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);
|
||||
Log.ForContext<T>().Information("User request to close lock of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// lock GUI
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
@ -187,10 +187,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
#else
|
||||
await SelectedBike.CloseLockAsync(this, stopPollingTask);
|
||||
#endif
|
||||
Log.ForContext<T>().Information("User locked {bike} successfully.", SelectedBike);
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} closed successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} can not be closed. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
|
@ -222,6 +223,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
// Message for parking bike
|
||||
if(IsEndRentalRequested == false)
|
||||
{
|
||||
Log.ForContext<T>().Information("User request to park bike {bikeId}.", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentalProcessCloseLockFinishedTitle,
|
||||
AppResources.MessageRentalProcessCloseLockFinishedText,
|
||||
|
|
|
@ -9,6 +9,7 @@ using TINK.View;
|
|||
using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand;
|
||||
using Serilog;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||
{
|
||||
|
@ -144,7 +145,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
/// <summary> Return bike. </summary>
|
||||
public async Task EndRentalAsync()
|
||||
{
|
||||
Log.ForContext<T>().Information("User requests to return bike {bike}.", SelectedBike);
|
||||
Log.ForContext<T>().Information("User request to end rental of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// lock GUI
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
@ -170,9 +171,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
try
|
||||
{
|
||||
currentLocationDto = await SelectedBike.GetLockedBikeLocationAsync(this);
|
||||
Log.ForContext<T>().Information("Location information for lock received successfully.");
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Information("Location information for lock can not be received. {@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
||||
|
@ -192,16 +195,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
currentLocationDto);
|
||||
Log.ForContext<T>().Information("Rental of bike {bikeId} was terminated successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Information("Rental of bike {bikeId} can not be terminated.", SelectedBike.Id);
|
||||
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);
|
||||
// No web.
|
||||
Log.ForContext<T>().Error("Copri server not reachable. No web.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -210,11 +215,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
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);
|
||||
// not at station.
|
||||
Log.ForContext<T>().Error("COPRI returned out of GEO fencing error. Position send to COPRI {position}.", currentLocationDto);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -223,8 +225,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
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);
|
||||
// no GPS data.
|
||||
Log.ForContext<T>().Error("COPRI returned a no-GPS-data error.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -233,8 +235,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
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);
|
||||
// COPRI exception.
|
||||
Log.ForContext<T>().Error("COPRI returned an error. {response}", copriException.Response);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -244,7 +246,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<T>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorEndRentalTitle,
|
||||
|
@ -304,19 +306,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
Message = feedback.Message
|
||||
},
|
||||
feedBackUri);
|
||||
Log.ForContext<T>().Information("Feedback for bike {bikeId} was submitted successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<T>().Information("Feedback for bike {bikeId} can not be submitted.", SelectedBike.Id);
|
||||
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);
|
||||
// Copri exception.
|
||||
Log.ForContext<T>().Debug("COPRI returned an error. {response}", copriException.Response);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<T>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<T>().Debug("{@exception}", exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
|
@ -332,10 +335,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<T>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<T>().Information("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
|
@ -344,7 +348,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Confirmation message that rental is ended
|
||||
Log.ForContext<T>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
Log.ForContext<T>().Information("Rental of bike {bikeId} was terminated successfully.", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
String.Format(AppResources.MessageRentalProcessEndRentalFinishedTitle, SelectedBike.Id),
|
||||
|
|
|
@ -32,8 +32,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionReturn, // Copri button text "Miete beenden"
|
||||
true, // Show button to enabled returning of bike.
|
||||
AppResources.ActionEndRental, // End Rental
|
||||
true, // Show button "End Rental"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -44,8 +44,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
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.
|
||||
LockitButtonText = AppResources.ActionOpenLock; // Open Lock
|
||||
IsLockitButtonVisible = true; // Show button "Open Lock"
|
||||
|
||||
_endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
|
||||
selectedBike,
|
||||
|
@ -98,28 +98,28 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedClosed>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
Log.ForContext<BookedClosed>().Information("User request to unlock booked bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// open lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<BookedClosed>().Information("Lock of bike {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<BookedClosed>().Information("Lock of bike {bikeId} can not be opened.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -128,7 +128,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -137,7 +137,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -147,7 +147,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -156,7 +156,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedClosed>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -171,36 +171,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedClosed>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Information("Battery state of lock from {bikeId} can not be read. ", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedClosed>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -212,42 +207,42 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<BookedClosed>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<BookedClosed>().Debug("{response}.", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<BookedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedClosed>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true;
|
||||
LockitButtonText = AppResources.ActionSearchLock; // Search Lock
|
||||
IsLockitButtonVisible = true; // show button "Search Lock"
|
||||
}
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
@ -69,7 +69,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedDisconnected>().Information("Request to search {bike} detected.", SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Information("User request to connect to lock of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
@ -81,16 +81,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Repeat booking to get a new seed/ k_user value.
|
||||
await ConnectorFactory(IsConnected).Command.CalculateAuthKeys(SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Information("Calculation of AuthKeys successfully.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Calculation of AuthKeys failed.");
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedDisconnected>().Information("User selected booked bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<BookedDisconnected>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -99,7 +101,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Error("User selected booked bike {l_oId} to connect to lock. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<BookedDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
|
@ -115,6 +117,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
// Reconnect to lock
|
||||
LockInfoTdo result = null;
|
||||
var continueConnect = true;
|
||||
var retryCount = 1;
|
||||
|
@ -127,15 +130,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
result = await LockService.ConnectAsync(
|
||||
new LockInfoAuthTdo.Builder { Id = SelectedBike.LockInfo.Id, Guid = SelectedBike.LockInfo.Guid, K_seed = SelectedBike.LockInfo.Seed, K_u = SelectedBike.LockInfo.UserKey }.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(retryCount));
|
||||
Log.ForContext<BookedDisconnected>().Information("Connected to lock of bike {bikeId} successfully. Value is {lockState}.", SelectedBike.Id, SelectedBike.LockInfo.State);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Information("Connection to lock of bike {bikeId} failed. ", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is ConnectBluetoothNotOnException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Bluetooth not on.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockBluetoothNotOn,
|
||||
|
@ -144,7 +148,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is ConnectLocationPermissionMissingException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Location permission missing.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorNoLocationPermission,
|
||||
|
@ -153,7 +157,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is ConnectLocationOffException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Location services not on.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockLocationOff,
|
||||
|
@ -162,7 +166,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is OutOfReachException)
|
||||
{
|
||||
continueConnect = false;
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Error("Lock is out of reach.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
|
@ -170,7 +174,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Error("Lock can not be found. {Exception}", exception);
|
||||
Log.ForContext<BookedDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
string message;
|
||||
if (retryCount < 2)
|
||||
|
@ -190,7 +194,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
continueConnect = await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
message,
|
||||
"",
|
||||
string.Empty,
|
||||
AppResources.MessageAnswerRetry,
|
||||
AppResources.MessageAnswerCancel);
|
||||
}
|
||||
|
@ -212,7 +216,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
if (!(result?.State is LockitLockingState lockingState))
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Error("Locking state of bike {bikeId} not found.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
|
@ -228,11 +232,75 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
// get current locking state
|
||||
var state = lockingState.GetLockingState();
|
||||
SelectedBike.LockInfo.State = state;
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
Log.ForContext<BookedDisconnected>().Information($"State for bike {SelectedBike.Id} updated successfully. Value is {SelectedBike.LockInfo.State}.");
|
||||
// get current lock charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<BookedDisconnected>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// get lock infos
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<BookedDisconnected>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<BookedDisconnected>().Debug("Copri server not reachable, no web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<BookedDisconnected>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
|
|
|
@ -30,8 +30,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionClose, // Copri button text: "Close lock"
|
||||
true, // Show button to allow user to close lock.
|
||||
AppResources.ActionCloseLock, // Close Lock
|
||||
true, // Show button "Close Lock"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
|
|
@ -9,7 +9,6 @@ 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;
|
||||
|
@ -35,8 +34,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndPause, // Schloss öffnen und Miete fortsetzen.
|
||||
true, // Show button to enabled returning of bike.
|
||||
AppResources.ActionOpenLock, // Open Lock
|
||||
true, // Show button "Open Lock"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -47,8 +46,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
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.
|
||||
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
|
||||
IsLockitButtonVisible = true; // Show button "Close Lock"
|
||||
|
||||
_closeLockActionViewModel = new CloseLockActionViewModel<BookedOpen>(
|
||||
selectedBike,
|
||||
|
@ -69,34 +68,70 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
{
|
||||
await _closeLockActionViewModel.CloseLockAsync();
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
if (_closeLockActionViewModel.IsEndRentalRequested == false)
|
||||
{
|
||||
return Create(
|
||||
SelectedBike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
GeolocationService,
|
||||
LockService,
|
||||
ViewUpdateManager,
|
||||
SmartDevice,
|
||||
ViewService,
|
||||
BikesViewModel,
|
||||
ActiveUser);
|
||||
}
|
||||
|
||||
var _endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
|
||||
SelectedBike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
LockService,
|
||||
ViewUpdateManager,
|
||||
ViewService,
|
||||
BikesViewModel);
|
||||
|
||||
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> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
Log.ForContext<BookedUnknown>().Information("User request to unlock bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// open lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<BookedUnknown>().Information("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock is out of reach");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -105,7 +140,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -114,7 +149,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -124,7 +159,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -133,7 +168,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<BookedUnknown>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -148,35 +183,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<BookedUnknown>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<BookedUnknown>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -188,36 +219,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<BookedUnknown>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
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);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<BookedUnknown>().Information("Copri server not reachable. No web.");
|
||||
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. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<BookedUnknown>().Information("{response}", 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);
|
||||
Log.ForContext<BookedUnknown>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
|
|
@ -4,7 +4,6 @@ using Serilog;
|
|||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
|
@ -33,8 +32,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionRequest, // Copri text: "Rad reservieren"
|
||||
true, // Show copri button to enable reserving and opening
|
||||
AppResources.ActionReserveBike, // Reserve Bike
|
||||
true, // Show button "Reserve Bike"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -46,17 +45,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false; // If bike is not reserved/ booked app can not connect to lock
|
||||
IsLockitButtonVisible = false; // If bike is not reserved/ rented app can not connect to lock
|
||||
}
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReserveBookAndOpen();
|
||||
/// <summary>Reserve bike, connect to lock, open lock and rent bike.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReserveRentBikeAndOpenLock();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> ReserveBookAndOpen()
|
||||
/// <summary>Reserve and rent bike.</summary>
|
||||
public async Task<IRequestHandler> ReserveRentBikeAndOpenLock()
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("User request to reserve bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really reserve bike?
|
||||
|
@ -72,13 +72,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected centered bike {bike} in order to reserve but action was canceled.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User canceled request to reserve bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<DisposableDisconnected>().Information("Request to book and open lock for bike {bike} detected.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
|
@ -87,18 +86,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// reserve bike
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoReserve(SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User reserved bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("Request to reserve bike {bikeId} declined.", SelectedBike.Id);
|
||||
if (exception is BookingDeclinedException)
|
||||
{
|
||||
// Too many bikes booked.
|
||||
Log.ForContext<DisposableDisconnected>().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Maximum count of bikes {exception.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageHintTitle,
|
||||
|
@ -109,7 +110,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected centered bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -118,7 +119,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected centered bike {bike} but reserving failed. {@l_oException}", SelectedBike, exception);
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReservingBikeTitle,
|
||||
|
@ -143,19 +144,21 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
result = await LockService.ConnectAsync(
|
||||
new LockInfoAuthTdo.Builder { Id = SelectedBike.LockInfo.Id, Guid = SelectedBike.LockInfo.Guid, K_seed = SelectedBike.LockInfo.Seed, K_u = SelectedBike.LockInfo.UserKey }.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(1));
|
||||
Log.ForContext<DisposableDisconnected>().Information("Connected to lock of bike {bikeId} successfully. Value is {lockState}.", SelectedBike.Id, SelectedBike.LockInfo.State);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Connection to lock of bike {bikeId} failed.");
|
||||
|
||||
// Do not display any messages here, because search is implicit.
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock state can not be retrieved, lock is out of reach. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextLockIsOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock state can not be retrieved. {Exception}", exception);
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextLockNotFound;
|
||||
}
|
||||
|
||||
|
@ -167,11 +170,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current locking state
|
||||
SelectedBike.LockInfo.State = result?.State?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
if (SelectedBike.LockInfo.State == LockingState.UnknownDisconnected)
|
||||
{
|
||||
// Do not display any messages here, because search is implicit.
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock is still not connected.");
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
|
@ -182,32 +186,34 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock found {bike} successfully.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
alertResult = SelectedBike.LockInfo.State != LockingState.Open
|
||||
? await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo)
|
||||
: await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected recently requested bike {bike} in order to reserve but did deny to book bike.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User request to not book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableDisconnected>().Information("Disconnected from lock of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
|
@ -219,23 +225,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("User selected recently requested bike {bike} in order to book.", SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("User request to book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
}
|
||||
Log.ForContext<DisposableDisconnected>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
Log.ForContext<DisposableDisconnected>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// 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);
|
||||
Log.ForContext<DisposableDisconnected>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -244,11 +258,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -260,107 +274,96 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
// Unlock bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Lock can not be opened. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock is out of reach.");
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<DisposableDisconnected>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Lock is out of reach");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -372,36 +375,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<DisposableDisconnected>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<DisposableDisconnected>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<DisposableDisconnected>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<DisposableDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableDisconnected>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
|
|
@ -9,6 +9,7 @@ using TINK.MultilingualResources;
|
|||
using TINK.Repository.Exception;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.CopriApi.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
|
||||
|
@ -45,8 +46,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseOrBook,
|
||||
true, // Show copri button to enable reserving
|
||||
AppResources.ActionRentBike, // Rent Bike
|
||||
true, // Show button "Rent Bike"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -57,226 +58,362 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false;
|
||||
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
|
||||
IsLockitButtonVisible = true; // Show button "Close Lock"
|
||||
}
|
||||
|
||||
/// <summary>Books bike by reserving bike, opening lock and booking bike.</summary>
|
||||
/// <summary>Rents bike by reserving bike and renting bike.</summary>
|
||||
/// <returns>Next request handler.</returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockOrDoBook();
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await RentBike();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
|
||||
/// <summary>Books bike by reserving bike, opening lock and booking bike.</summary>
|
||||
/// <summary>Closes Lock.</summary>
|
||||
/// <returns>Next request handler.</returns>
|
||||
public async Task<IRequestHandler> CloseLockOrDoBook()
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
public async Task<IRequestHandler> RentBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<DisposableOpen>().Information("User request to book bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Ask whether to really book bike or close lock?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
String.Format(AppResources.QuestionCloseOrBook, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.ActionBook,
|
||||
AppResources.ActionClose);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// Close lock
|
||||
Log.ForContext<DisposableOpen>().Information("User selected disposable bike {bike} in order to close lock.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
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.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<DisposableOpen>().Information("Request to book bike {bike}.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify copri about unlock action in order to start booking.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
//reserve bike prior to book.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
await ConnectorFactory(IsConnected).Command.DoReserve(SelectedBike);
|
||||
Log.ForContext<DisposableOpen>().Information("User reserved bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
Log.ForContext<DisposableOpen>().Information("Request to reserve bike {bikeId} declined.", SelectedBike.Id);
|
||||
if (exception is BookingDeclinedException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableOpen>().Information("User selected requested bike {l_oId} but reserving failed (Copri server not reachable).", SelectedBike.Id);
|
||||
// Too many bikes booked.
|
||||
Log.ForContext<DisposableOpen>().Error("Maximum count of bikes {exception.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.MessageHintTitle,
|
||||
string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableOpen>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<DisposableOpen>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
string.Format(l_oException.Message, AppResources.ErrorTryAgain),
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReservingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// If booking failed lock bike again because bike is only reserved.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Locking bike after booking failure failed. {Exception}", exception);
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableOpen>().Information("Lock from bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
Log.ForContext<DisposableOpen>().Information("Lock from bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Book bike
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
Log.ForContext<DisposableOpen>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<DisposableOpen>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableOpen>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableOpen>().Error("Lock from bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock from bike {bikeId} can not be disconnected. {Exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableOpen>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
// Get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<DisposableOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// Update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<DisposableOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<DisposableOpen>().Debug("Copri server not reachable. No web");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<DisposableOpen>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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.
|
||||
BikesViewModel.IsIdle = true;
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary> Request is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
Log.ForContext<DisposableOpen>().Information("User request to close lock of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<DisposableOpen>().Information("Lock of bike {bikeId} closed successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Information("Lock of bike {bikeId} can not be closed.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Lock is moving.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoltBlockedException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} can not be read", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedOpen>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<DisposableOpen>().Error("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<DisposableOpen>().Error("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,11 +64,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
try
|
||||
{
|
||||
// Switch to map page
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
|
|
@ -38,8 +38,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCancelRequest, // Copri button text: "Reservierung abbrechen"
|
||||
true, // Show button to enable canceling reservation.
|
||||
AppResources.ActionCancelReservation, // Cancel Reservation
|
||||
true, // Show button "Cancel Reservation"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -50,37 +50,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndBook; // Button text: "Schloss öffnen & Rad mieten"
|
||||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
LockitButtonText = AppResources.ActionOpenLockAndRentBike; // Open Lock & Rent Bike
|
||||
IsLockitButtonVisible = true; // Show button "Open Lock & Rent Bike"
|
||||
}
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
/// <summary> Open lock and book bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLockAndDoBook();
|
||||
/// <summary> Open lock and rent bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLockAndRentBike();
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> CancelReservation()
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("User request to cancel reservation of bike {bikeId}", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
if (result == false)
|
||||
{
|
||||
// User aborted cancel process
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation but action was canceled.", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Information("User canceled request to cancel reservation of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
@ -90,15 +89,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("User canceled reservation of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("Canceling reservation of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Error("Invalid auth. response.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
|
@ -109,7 +109,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// 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);
|
||||
Log.ForContext<ReservedClosed>().Error("Copri server not reachable.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
|
@ -117,7 +117,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedClosed>().Error("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
|
@ -132,18 +132,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BikesViewModel>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch (Exception disconnectException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, disconnectException);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
|
@ -154,9 +152,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Open lock and book bike. </summary>
|
||||
public async Task<IRequestHandler> OpenLockAndDoBook()
|
||||
/// <summary> Open lock and rent bike. </summary>
|
||||
public async Task<IRequestHandler> OpenLockAndRentBike()
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("User request to book and open bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
|
@ -169,13 +168,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {bike} in order to book but action was canceled.", SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("User canceled request to book bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {bike} in order to book but action was canceled.", SelectedBike);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
@ -186,15 +183,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
Log.ForContext<ReservedClosed>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
Log.ForContext<ReservedClosed>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
|
@ -203,12 +201,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<ReservedClosed>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -221,18 +219,19 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Unlock bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from {bikeId} opened successfully.",SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -242,7 +241,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -252,7 +251,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Bold status is unknown.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -263,7 +262,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock reports that it is still closed.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -273,7 +272,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorTryAgain,
|
||||
|
@ -285,47 +284,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedClosed>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedClosed>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -337,36 +320,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<ReservedClosed>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedClosed>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedClosed>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
|
|
@ -32,8 +32,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCancelRequest,
|
||||
true, // Show button to enable canceling reservation.
|
||||
AppResources.ActionCancelReservation,
|
||||
true, // Show button "Cancel Reservation".
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -45,19 +45,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true; // Show button to search lock.
|
||||
IsLockitButtonVisible = true; // Show button "Search Lock"
|
||||
}
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
/// <summary> Connect to reserved bike ask whether to book bike or not and if yes open lock. </summary>
|
||||
/// <summary> Connect to reserved bike ask whether to rent bike or not and if yes open lock. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ConnectLockAndBook();
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ConnectLockAndRentBike();
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> CancelReservation()
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("User request to cancel reservation of bike {bikeId}", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
|
@ -69,13 +70,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (alertResult == false)
|
||||
{
|
||||
// User aborted cancel process
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {l_oId} in order to cancel reservation but action was canceled.", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User canceled request to cancel reservation of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
@ -85,14 +84,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User canceled reservation of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {bikeId} but cancel reservation failed.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Invalid auth. response.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
|
@ -102,7 +103,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// 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);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Copri server not reachable.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
|
@ -110,7 +111,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("{@exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
|
@ -125,8 +126,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
@ -134,12 +133,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Connect to reserved bike ask whether to book bike or not and if yes open lock. </summary>
|
||||
/// <summary> Connect to reserved bike ask whether to rent bike or not and if yes open lock. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> ConnectLockAndBook()
|
||||
public async Task<IRequestHandler> ConnectLockAndRentBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<ReservedDisconnected>().Information("Request to search for {bike} detected.", SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User request to connect to lock of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
@ -151,6 +150,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Repeat reservation to get a new seed/ k_user value.
|
||||
await ConnectorFactory(IsConnected).Command.CalculateAuthKeys(SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Calculation of AuthKeys successfully.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
@ -160,7 +160,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected requested bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Calculation of AuthKeys failed (Copri server not reachable).");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
|
@ -169,7 +169,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected requested bike {l_oId} to scan for lock. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Calculation of AuthKeys failed. {@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorConnectLockTitle,
|
||||
|
@ -204,6 +204,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
K_u = SelectedBike.LockInfo.UserKey
|
||||
}.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(retryCount));
|
||||
Log.ForContext<ReservedDisconnected>().Information("Connected to lock of bike {bikeId} successfully. Value is {lockState}.", SelectedBike.Id, SelectedBike.LockInfo.State);
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
@ -217,6 +219,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockBluetoothNotOn,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, bluetooth is off.", SelectedBike.Id);
|
||||
}
|
||||
else if (exception is ConnectLocationPermissionMissingException)
|
||||
{
|
||||
|
@ -226,6 +229,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorNoLocationPermission,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, location permissions are missing.", SelectedBike.Id);
|
||||
}
|
||||
else if (exception is ConnectLocationOffException)
|
||||
{
|
||||
|
@ -235,6 +239,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockLocationOff,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, location services are off.", SelectedBike.Id);
|
||||
}
|
||||
else if (exception is OutOfReachException)
|
||||
{
|
||||
|
@ -244,10 +249,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.ErrorConnectLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Connection to lock of bike {bikeId} failed, lock is out of reach.", SelectedBike.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock can not be found. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock of bike {bikeId} can not be found. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
string message;
|
||||
if (retryCount < 2)
|
||||
|
@ -284,7 +290,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
if (!(result?.State is LockitLockingState lockingState))
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock for bike {bikeId} not found.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
|
@ -300,34 +306,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = lockingState.GetLockingState();
|
||||
// get current locking state
|
||||
var state = lockingState.GetLockingState();
|
||||
SelectedBike.LockInfo.State = state;
|
||||
SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid();
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information($"State for bike {SelectedBike.Id} updated successfully. Value is {SelectedBike.LockInfo.State}.");
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Ask whether to really book bike?
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
var alertResult
|
||||
= SelectedBike.LockInfo.State != LockingState.Open
|
||||
? await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo)
|
||||
: await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionBookBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected recently requested bike {bike} in order to reserve but did deny to book bike.", SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("User denied to book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock of bike {bikeId} can not be disconnected. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
@ -340,23 +352,30 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected recently requested bike {bike} in order to book.", SelectedBike);
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User request to book reserved bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
}
|
||||
Log.ForContext<ReservedDisconnected>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// 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);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Booking of bike {bikeId} failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
|
@ -365,11 +384,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Booking of bike {bikeId} failed. {@exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -382,102 +401,91 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
// Unlock bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. Bold status is unknown. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock can not be opened. {Exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
if (SelectedBike.LockInfo.State != LockingState.Open)
|
||||
{
|
||||
// Opening lock failed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. Bold is blocked. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. Bold status is unknown. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStatusUnknown,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Lock from {bikeId} can not be opened. lock reports state closed. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosed,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Lock from {bikeId} can not be opened. {Exception}", SelectedBike.Id, exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedDisconnected>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Battery state of lock from {bikeId} can not be read, bike out of range. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("Battery state can not be read. {Exception}", exception);
|
||||
Log.ForContext<ReservedDisconnected>().Error("Battery state of lock from {bikeId} can not be read. {Exception}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
|
@ -495,37 +503,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedDisconnected>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<ReservedDisconnected>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedDisconnected>().Debug("{response}.", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedDisconnected>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedDisconnected>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
|
|
|
@ -12,6 +12,7 @@ using TINK.Services.BluetoothLock.Exception;
|
|||
using TINK.Services.CopriApi.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -37,8 +38,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseOrBook,
|
||||
true, // Show button to enable canceling reservation.
|
||||
AppResources.ActionCloseLock,
|
||||
true, // Show button "Close Lock"
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -49,246 +50,133 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = "Alarm/ Sounds verwalten";
|
||||
IsLockitButtonVisible = activeUser.DebugLevel > 0; // Will be visible in future version of user with leveraged privileges.
|
||||
LockitButtonText = activeUser.DebugLevel.HasFlag(Model.User.Account.Permissions.ManageAlarmAndSounds) ? "Alarm/ Sounds verwalten" : AppResources.ActionRentBike;
|
||||
IsLockitButtonVisible = true; // Only users with special permissions are allowed to set alarm. Regular user gets options "Rent Bike"
|
||||
}
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockOrDoBook();
|
||||
/// <summary> Close lock. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLock();
|
||||
|
||||
/// <summary> Manage sound/ alarm settings. </summary>
|
||||
/// <summary> Manage sound/ alarm settings or Rent bike. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ManageLockSettings();
|
||||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
=> ActiveUser.DebugLevel.HasFlag(Model.User.Account.Permissions.ManageAlarmAndSounds)
|
||||
? await ManageLockSettings()
|
||||
: await RentBike();
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> CloseLockOrDoBook()
|
||||
/// <summary> Rent bike. </summary>
|
||||
public async Task<IRequestHandler> RentBike()
|
||||
{
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<ReservedOpen>().Information("User request to book bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCloseOrBook, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.ActionClose,
|
||||
AppResources.ActionBook);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User decided to book
|
||||
Log.ForContext<ReservedOpen>().Information("User selected requested bike {bike} in order to book.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify copri about unlock action in order to start booking.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedOpen>().Information("User selected requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
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.DisplayAdvancedAlert(
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
l_oException.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// If booking failed lock bike again because bike is only reserved.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("Locking bike after booking failure failed. {Exception}", exception);
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedOpen>().Information("User booked bike {bike} successfully.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
}
|
||||
|
||||
// Close lock and cancel reservation.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock bike is moving. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
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.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
string.Format(AppResources.ErrorCloseLock, exception.Message),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||||
// Book bike
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
await ConnectorFactory(IsConnected).Command.DoBookAsync(SelectedBike, LockingAction.Open);
|
||||
Log.ForContext<ReservedOpen>().Information("User booked bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = String.Empty;
|
||||
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// 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.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<ReservedOpen>().Information("Booking of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// 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);
|
||||
Log.ForContext<ReservedOpen>().Error("Copri server not reachable.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoConnectionTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, exception);
|
||||
Log.ForContext<ReservedOpen>().Error("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorRentingBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
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);
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedOpen>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
// get current charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
Log.ForContext<ReservedOpen>().Debug("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Debug("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedOpen>().Debug("{response}", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
|
@ -301,7 +189,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// Close lock
|
||||
Log.ForContext<ReservedOpen>().Information("User selected disposable bike {bike} in order to manage sound/ alarm settings.", SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("User selected bike {bikeId} in order to manage sound/ alarm settings.", SelectedBike.Id);
|
||||
|
||||
// Alarm and sounds are on, toggle to off.
|
||||
// Switch off sound.
|
||||
|
@ -411,5 +299,208 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
// Stop polling before requesting bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
Log.ForContext<ReservedOpen>().Information("User request to close lock of bike {bikeId}.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} closed successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} can not be closed.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockOutOfReach,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is moving.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorLockMoving,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoltBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoltBlocked,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
{
|
||||
SelectedBike.LockInfo.VersionInfo = new VersionInfo.Builder
|
||||
{
|
||||
FirmwareVersion = versionTdo.FirmwareVersion,
|
||||
HardwareVersion = versionTdo.HardwareVersion,
|
||||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed .", SelectedBike.Id);
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Information("Copri server not reachable. No web.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedOpen>().Debug("{response}.", copriException.Response);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
// Ask whether to cancel reservation
|
||||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (result == true)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("User request to cancel reservation of bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoCancelReservation(SelectedBike);
|
||||
Log.ForContext<ReservedOpen>().Information("User canceled reservation of bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
Log.ForContext<ReservedOpen>().Information("Canceling reservation of bike {bikeId} failed.", SelectedBike.Id);
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedOpen>().Debug("Invalid auth. response.");
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorAccountInvalidAuthorization,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException
|
||||
|| exception is RequestNotCachableException)
|
||||
{
|
||||
// No web.
|
||||
Log.ForContext<ReservedOpen>().Debug("Copri server not reachable. No web.");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
AppResources.ErrorNoWeb,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Debug("{@exception}.", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorCancelReservationTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorTryAgain,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect lock.
|
||||
if (SelectedBike.LockInfo.State == LockingState.Closed)
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} disconnected successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Information("Lock of bike {bikeId} can not be disconnected. {@exception}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndBook, // BT button text "Schloss öffnen und Rad mieten."
|
||||
false, // Show button to enabled returning of bike.
|
||||
AppResources.ActionOpenLockAndRentBike, // Open Lock & Rent Bike
|
||||
true, // Show button "Open Lock & Rent Bike.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -47,7 +47,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen"
|
||||
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
|
||||
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>(
|
||||
|
@ -57,34 +57,46 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
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 (and return bike).</summary>
|
||||
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()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to unlock bike {bikeId}.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopAsync();
|
||||
|
||||
// open lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
ILockService btLock;
|
||||
ILockService btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
try
|
||||
{
|
||||
btLock = LockService[SelectedBike.LockInfo.Id];
|
||||
SelectedBike.LockInfo.State = (await btLock.OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
Log.ForContext<ReservedUnknown>().Information("Lock from {bikeId} opened successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("Lock from {bikeId} can not be opened.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock is out of reach.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -94,7 +106,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. bold is blocked. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Bold is blocked.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -104,7 +116,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else if (exception is CouldntOpenBoldStatusIsUnknownException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. lock reports state unkwnown. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock reports that state is still unknown.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -115,7 +127,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock reports that it is closed.");
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -125,7 +137,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
Log.ForContext<ReservedUnknown>().Debug("{@exception}", exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
|
@ -140,35 +152,31 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// get current charging level
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await btLock.GetBatteryPercentageAsync();
|
||||
Log.ForContext<ReservedUnknown>().Information("Battery state of lock from {bikeId} read successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Information("Battery state of lock from {bikeId} can not be read.", SelectedBike.Id);
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Battery state can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock is out of reach.");
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Battery state can not be read. {Exception}", exception);
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// get lock infos.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
var versionTdo = btLock.VersionInfo;
|
||||
if (versionTdo != null)
|
||||
|
@ -180,36 +188,36 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
LockVersion = versionTdo.LockVersion,
|
||||
}.Build();
|
||||
}
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
// update backend
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
Log.ForContext<ReservedUnknown>().Information("Backend updated for bike {bikeId} successfully.", SelectedBike.Id);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Information("Updating backend for bike {bikeId} failed.", SelectedBike.Id);
|
||||
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);
|
||||
|
||||
// No web.
|
||||
Log.ForContext<ReservedUnknown>().Debug("Copri server not reachable. No web.");
|
||||
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. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
// Copri exception.
|
||||
Log.ForContext<ReservedUnknown>().Debug("{response}", 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);
|
||||
Log.ForContext<ReservedUnknown>().Debug("{@exception}", exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartAsync();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
@ -217,20 +225,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the view model for close action.
|
||||
/// </summary>
|
||||
private CloseLockActionViewModel<BookedOpen> _closeLockActionViewModel;
|
||||
|
||||
/// <summary> Close lock (and return bike).</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2()
|
||||
{
|
||||
await _closeLockActionViewModel.CloseLockAsync();
|
||||
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes the close lock progress.
|
||||
/// </summary>
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndPause;
|
||||
LockitButtonText = AppResources.ActionOpenLockAndPause;
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndPause; // Lock is open but show button anyway to be less prone to errors.
|
||||
LockitButtonText = AppResources.ActionOpenLockAndPause; // Lock is open but show button anyway to be less prone to errors.
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndBook, // Button text: "Schloss öffnen & Rad mieten"
|
||||
AppResources.ActionOpenLockAndRentBike, // Button text: "Schloss öffnen & Rad mieten"
|
||||
true, // Show copri button to enable booking and opening
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -39,7 +39,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionRequest; // Copri text: "Rad reservieren"
|
||||
LockitButtonText = AppResources.ActionReserveBike; // Copri text: "Rad reservieren"
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCancelRequest, // Copri button text: "Reservierung abbrechen"
|
||||
AppResources.ActionCancelReservation, // Copri button text: "Reservierung abbrechen"
|
||||
true, // Show button to enable canceling reservation.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -44,7 +44,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionOpenAndBook; // Button text: "Schloss öffnen & Rad mieten"
|
||||
LockitButtonText = AppResources.ActionOpenLockAndRentBike; // Button text: "Schloss öffnen & Rad mieten"
|
||||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
}
|
||||
|
||||
|
|
|
@ -500,7 +500,7 @@ namespace TINK.ViewModel.Bikes
|
|||
/// Transforms bikes view model object to string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public new string ToString()
|
||||
public override string ToString()
|
||||
{
|
||||
var l_oToString = string.Empty;
|
||||
foreach (var item in Items)
|
||||
|
|
|
@ -167,36 +167,20 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
/// <summary> Command object to bind login page redirect link to view model.</summary>
|
||||
public System.Windows.Input.ICommand ContactSupportClickedCommand
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenSupportPageAsync());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenSupportPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Command object to bind login page redirect link to view model.</summary>
|
||||
public System.Windows.Input.ICommand LoginRequiredHintClickedCommand
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenLoginPageAsync());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Opens login page. </summary>
|
||||
#if USEFLYOUT
|
||||
public void OpenLoginPageAsync()
|
||||
#else
|
||||
public async Task OpenLoginPageAsync()
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Switch to map page
|
||||
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.LoginPage);
|
||||
#else
|
||||
await ViewService.ShowPage("//LoginPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
@ -206,11 +190,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
}
|
||||
|
||||
/// <summary> Opens support. </summary>
|
||||
#if USEFLYOUT
|
||||
public void OpenSupportPageAsync()
|
||||
#else
|
||||
public async Task OpenSupportPageAsync()
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -222,11 +202,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
// Switch to map page
|
||||
|
||||
#if USEFLYOUT
|
||||
ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingFeedbackAndContact);
|
||||
#else
|
||||
await ViewService.ShowPage("//ContactPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
@ -257,7 +233,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
ActionText = AppResources.ActivityTextBikesAtStationGetBikes;
|
||||
|
||||
var bikesAll = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
|
||||
var bikesAll = await ConnectorFactory(IsConnected).Query.GetBikesAsync(Station?.OperatorUri);
|
||||
|
||||
Exception = bikesAll.Exception; // Update communication error from query for bikes at station.
|
||||
|
||||
|
|
|
@ -237,28 +237,16 @@ namespace TINK.ViewModel.Info
|
|||
|
||||
/// <summary> Command object to bind login button to view model. </summary>
|
||||
public ICommand OnSelectStationRequest
|
||||
#if USEFLYOUT
|
||||
=> new Xamarin.Forms.Command(() => OpenSelectStationPage());
|
||||
#else
|
||||
=> new Xamarin.Forms.Command(async () => await OpenSelectStationPageAsync());
|
||||
#endif
|
||||
|
||||
/// <summary> Opens login page. </summary>
|
||||
#if USEFLYOUT
|
||||
public void OpenSelectStationPage()
|
||||
#else
|
||||
public async Task OpenSelectStationPageAsync()
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Switch to map page
|
||||
|
||||
#if USEFLYOUT
|
||||
ViewService.PushAsync(ViewTypes.SelectStationPage);
|
||||
#else
|
||||
await ViewService.PushAsync(ViewTypes.SelectStationPage);
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
|
|
@ -12,9 +12,6 @@ using System.Threading.Tasks;
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms.GoogleMaps;
|
||||
using System.Collections.ObjectModel;
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.Services.Permissions;
|
||||
using Xamarin.Essentials;
|
||||
using System.Threading;
|
||||
|
@ -63,10 +60,6 @@ namespace TINK.ViewModel.Contact
|
|||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigation m_oNavigation;
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigationMasterDetail;
|
||||
#endif
|
||||
private ObservableCollection<Pin> pins;
|
||||
|
||||
public ObservableCollection<Pin> Pins
|
||||
|
@ -139,21 +132,10 @@ namespace TINK.ViewModel.Contact
|
|||
m_oNavigation = navigation
|
||||
?? throw new ArgumentException("Can not instantiate map page view model- object. No navigation service available.");
|
||||
|
||||
#if USEFLYOUT
|
||||
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
|
||||
#endif
|
||||
|
||||
IsConnected = TinkApp.GetIsConnected();
|
||||
}
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail
|
||||
{
|
||||
set { m_oNavigationMasterDetail = value; }
|
||||
}
|
||||
#endif
|
||||
|
||||
public Command<PinClickedEventArgs> PinClickedCommand => new Command<PinClickedEventArgs>(
|
||||
args =>
|
||||
{
|
||||
|
@ -461,19 +443,9 @@ namespace TINK.ViewModel.Contact
|
|||
IsMapPageEnabled = false;
|
||||
|
||||
TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId)
|
||||
?? new Model.Stations.StationNS.Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
?? new Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
m_oNavigation.ShowPage(
|
||||
typeof(BikesAtStationPage),
|
||||
p_strStationName);
|
||||
#else
|
||||
#if USEFLYOUT
|
||||
// Show page.
|
||||
ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingContactPageTitle);
|
||||
#else
|
||||
await ViewService.ShowPage("//ContactPage");
|
||||
#endif
|
||||
IsMapPageEnabled = true;
|
||||
ActionText = string.Empty;
|
||||
}
|
||||
|
@ -489,7 +461,6 @@ namespace TINK.ViewModel.Contact
|
|||
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TINK.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.Info.BikeInfo
|
||||
{
|
||||
public class BikeInfoViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference on view service to show modal notifications and to perform navigation.
|
||||
/// </summary>
|
||||
private readonly IViewService m_oViewService;
|
||||
|
||||
|
||||
/// <param name="p_oViewService">Interface to actuate methods on GUI.</param>
|
||||
public BikeInfoViewModel(Func<string, ImageSource> imageSourceFunc, IViewService p_oViewService)
|
||||
{
|
||||
m_oViewService = p_oViewService
|
||||
?? throw new ArgumentException("Can not instantiate bike info page view model- object. No view available.");
|
||||
|
||||
CarouselItems = new List<CourouselPageItemViewModel>();
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Anleitung Benutzung Lastenräder",
|
||||
$"Diese Anleitung wird einmalig nach der Anmeldung angezeigt.\r\nZum Blättern auf die nächste Seite bitte nach links wischen.\r\nNach Anzeige aller Seiten kann die Anleitung geschlossen werden.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction()));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Zweirädriges TINK Rad: Hochklappen des Fahrradständers (1/3)",
|
||||
"So abgestellt hat das zweirädrige Transportrad einen sicheren Stand.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_stand1_image.4HJ5PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Zweirädriges TINK Rad: Hochklappen des Fahrradständers (2/3)",
|
||||
"Zum Weiterfahren das Transportrad nach vorne bewegen, bis kein Gewicht mehr auf dem Fahrradständer liegt.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_stand2_image.RIX2PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Zweirädriges TINK Rad: Hochklappen des Fahrradständers (3/3).",
|
||||
"Den Fahrradständer mit dem Fuß nach oben drücken, bis er hörbar am Magneten (Pfeil) einrastet. So fällt er unterwegs nicht herunter.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_stand3_image.FDR7PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Dreirädriges TINK Rad: Lösen und Aktivieren der Feststellbremse (1/3).",
|
||||
"Die Feststellbremse ist der graue Stift, der aus der Bremse herausragt. Ist sie aktiv, kann das Dreirad nicht wegrollen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_brake1_image.HZ17PY_678_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Dreirädriges TINK Rad: Lösen und Aktivieren der Feststellbremse (2/3).",
|
||||
"Lösen der Feststellbremse: Die Bremse vollständig anziehen, bis der Stift wieder auf seine ursprüngliche Position herausspringt.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_brake2_image.1YBAQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Dreirädriges TINK Rad: Lösen und Aktivieren der Feststellbremse (3/3).",
|
||||
"Aktivieren der Feststellbremse: Die Bremse vollständig anziehen und den Stift hineindrücken.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("trike_brake3_image.FJM2PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Höhenregulierung des Sattels (1/3).",
|
||||
"Hier im Bild ist der Hebel zum Einstellen des Sattels zu sehen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("seat1_image.ZQ65PY_680_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Höhenregulierung des Sattels (2/3).",
|
||||
"Durch Drücken des Hebels ist die Sattelhöhe frei verstellbar. Vergleichbar mit einem Bürostuhl bewegt sich der Sattel automatisch nach oben.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("seat2_image.QQZCQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Höhenregulierung des Sattels (3/3).",
|
||||
"Durch kräftiges Herunterdrücken des Sattels (und gleichzeitigem Betätigen des Hebels) kann der Sattel nach unten verstellt werden. Tipp: Eventuell draufsetzen und dann den Hebel betätigen, um den Sattel nach unten zu drücken.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("seat3_image.NQ5FQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Verbinden des Kindergurts (1/3).",
|
||||
"Der Gurt besteht aus drei Einzelteilen. Zunächst die oberen beiden Einzelstücke nehmen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("belt1_image.4XWCQY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Verbinden des Kindergurts (2/3).",
|
||||
"Die beiden Einzelstücke zusammenfügen.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("belt2_image.X3F1PY_679_382.png")));
|
||||
|
||||
CarouselItems.Add(
|
||||
new CourouselPageItemViewModel(
|
||||
"Verbinden des Kindergurts (3/3).",
|
||||
"Das obere und untere Teilstück verbinden (bis zum Einrasten). Lösen der Teilstücke durch Drücken auf den roten Knopf.",
|
||||
CarouselItems.Count + 1,
|
||||
() => CarouselItems.Count,
|
||||
() => CloseAction(),
|
||||
imageSourceFunc("belt3_image.DYOXPY_679_382.png")));
|
||||
}
|
||||
|
||||
/// <summary> Gets the carousel page items</summary>
|
||||
public IList<CourouselPageItemViewModel> CarouselItems { get; }
|
||||
|
||||
/// <summary> Command object to bind close button to view model. </summary>
|
||||
#if USEFLYOUT
|
||||
private Action CloseAction
|
||||
=> () => m_oViewService.ShowPage(ViewTypes.MapPage);
|
||||
#else
|
||||
private Action CloseAction
|
||||
=> async () => await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
using System;
|
||||
using System.Windows.Input;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.Info.BikeInfo
|
||||
{
|
||||
public class CourouselPageItemViewModel
|
||||
{
|
||||
public CourouselPageItemViewModel(
|
||||
string p_strTitle,
|
||||
string p_strLegend,
|
||||
int p_iCurrentPageIndex,
|
||||
Func<int> p_iPagesCountProvider,
|
||||
Action p_oCloseAction,
|
||||
ImageSource image = null)
|
||||
{
|
||||
Title = p_strTitle;
|
||||
IsImageVisble = image != null;
|
||||
if (IsImageVisble)
|
||||
{
|
||||
Image = image;
|
||||
}
|
||||
|
||||
DescriptionText = p_strLegend;
|
||||
CurrentPageIndex = p_iCurrentPageIndex;
|
||||
PagesCountProvider = p_iPagesCountProvider;
|
||||
CloseAction = p_oCloseAction;
|
||||
}
|
||||
|
||||
/// <summary> Gets the title of the navigation page. </summary>
|
||||
public string Title { get; }
|
||||
|
||||
public bool IsImageVisble { get; }
|
||||
|
||||
/// <summary> Gets the image. </summary>
|
||||
public ImageSource Image { get; }
|
||||
|
||||
/// <summary> Gets the text which describes the image. </summary>
|
||||
public string DescriptionText { get; }
|
||||
|
||||
/// <summary> Get the progress of the carousselling progress.</summary>
|
||||
public double ProgressValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var l_oCount = PagesCountProvider();
|
||||
|
||||
return l_oCount > 0 ? (double)CurrentPageIndex / l_oCount : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets if user can leave carousel page.</summary>
|
||||
public bool IsCloseVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return PagesCountProvider() == CurrentPageIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Command object to bind close button to view model. </summary>
|
||||
public ICommand OnCloseRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Command(() => CloseAction());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns one based index of the current page. </summary>
|
||||
private int CurrentPageIndex { get; }
|
||||
|
||||
/// <summary> Gets the count of carousel pages. </summary>
|
||||
private Func<int> PagesCountProvider { get; }
|
||||
|
||||
/// <summary> Action to actuate when close is invoked. </summary>
|
||||
private Action CloseAction { get; }
|
||||
}
|
||||
}
|
|
@ -24,10 +24,6 @@ namespace TINK.ViewModel
|
|||
/// </summary>
|
||||
private readonly IViewService m_oViewService;
|
||||
|
||||
#if BACKSTYLE
|
||||
/// <summary> Reference to navigation object to navigate back to map page when login succeeded. </summary>
|
||||
private INavigation m_oNavigation;
|
||||
#endif
|
||||
/// <summary> Reference on the tink app instance. </summary>
|
||||
private ITinkApp TinkApp { get; }
|
||||
|
||||
|
@ -103,12 +99,7 @@ namespace TINK.ViewModel
|
|||
public LoginPageViewModel(
|
||||
ITinkApp tinkApp,
|
||||
Action<string> openUrlInExternalBrowser,
|
||||
#if !BACKSTYLE
|
||||
IViewService p_oViewService)
|
||||
#else
|
||||
IViewService p_oViewService,
|
||||
INavigation p_oNavigation)
|
||||
#endif
|
||||
{
|
||||
TinkApp = tinkApp
|
||||
?? throw new ArgumentException("Can not instantiate map page view model- object. No tink app object available.");
|
||||
|
@ -118,10 +109,6 @@ namespace TINK.ViewModel
|
|||
|
||||
m_oViewService = p_oViewService
|
||||
?? throw new ArgumentException("Can not instantiate login page view model- object. No view available.");
|
||||
#if BACKSTYLE
|
||||
m_oNavigation = p_oNavigation
|
||||
?? throw new ArgumentException("Can not instantiate login page view model- object. No navigation service available.");
|
||||
#endif
|
||||
|
||||
m_strMailAddress = tinkApp.ActiveUser.Mail;
|
||||
m_strPassword = tinkApp.ActiveUser.Password;
|
||||
|
@ -250,11 +237,7 @@ namespace TINK.ViewModel
|
|||
/// <summary>
|
||||
/// User request to log in.
|
||||
/// </summary>
|
||||
#if BACKSTYLE
|
||||
public async void Login()
|
||||
#else
|
||||
public async Task Login()
|
||||
#endif
|
||||
{
|
||||
if (!IsLoginRequestAllowed)
|
||||
{
|
||||
|
@ -398,23 +381,14 @@ namespace TINK.ViewModel
|
|||
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.CARGOBIKE))
|
||||
{
|
||||
// No need to show "Anleitung TINK Räder" because user can not use tink.
|
||||
#if USEFLYOUT
|
||||
|
||||
m_oViewService.ShowPage(account.IsAgbAcknowledged ? ViewTypes.MapPage : ViewTypes.ManageAccountPage);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch to map page
|
||||
#if USEFLYOUT
|
||||
m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions);
|
||||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
|
@ -425,10 +399,6 @@ namespace TINK.ViewModel
|
|||
return;
|
||||
}
|
||||
|
||||
#if BACKSTYLE
|
||||
// Navigate back to map page.
|
||||
await m_oNavigation.PopToRootAsync();
|
||||
#endif
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
}
|
||||
|
|
|
@ -12,9 +12,6 @@ using System.Threading.Tasks;
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms.GoogleMaps;
|
||||
using System.Collections.ObjectModel;
|
||||
#if USEFLYOUT
|
||||
using TINK.View.MasterDetail;
|
||||
#endif
|
||||
using TINK.Settings;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
|
@ -28,9 +25,6 @@ using TINK.Model.State;
|
|||
using TINK.Model.Bikes.BikeInfoNS.BC;
|
||||
using TINK.Model.Stations.StationNS;
|
||||
|
||||
#if !TRYNOTBACKSTYLE
|
||||
#endif
|
||||
|
||||
namespace TINK.ViewModel.Map
|
||||
{
|
||||
public class MapPageViewModel : INotifyPropertyChanged
|
||||
|
@ -75,10 +69,6 @@ namespace TINK.ViewModel.Map
|
|||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigation m_oNavigation;
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary>Delegate to perform navigation.</summary>
|
||||
private INavigationMasterDetail m_oNavigationMasterDetail;
|
||||
#endif
|
||||
private ObservableCollection<Pin> pins;
|
||||
|
||||
public ObservableCollection<Pin> Pins
|
||||
|
@ -153,9 +143,6 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
m_oViewUpdateManager = new IdlePollingUpdateTaskManager();
|
||||
|
||||
#if USEFLYOUT
|
||||
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
|
||||
#endif
|
||||
|
||||
Polling = PollingParameters.NoPolling;
|
||||
|
||||
|
@ -180,13 +167,6 @@ namespace TINK.ViewModel.Map
|
|||
}
|
||||
}
|
||||
|
||||
#if USEFLYOUT
|
||||
/// <summary> Delegate to perform navigation.</summary>
|
||||
public INavigationMasterDetail NavigationMasterDetail
|
||||
{
|
||||
set { m_oNavigationMasterDetail = value; }
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Counts the number of reserved or occupied bikes -> visualized in MyBikes-Icon
|
||||
/// </summary>
|
||||
|
@ -742,13 +722,8 @@ namespace TINK.ViewModel.Map
|
|||
IsMapPageEnabled = false;
|
||||
|
||||
TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId)
|
||||
?? new Model.Stations.StationNS.Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
?? new Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updated in background task.
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
m_oNavigation.ShowPage(
|
||||
typeof(BikesAtStationPage),
|
||||
p_strStationName);
|
||||
#else
|
||||
{
|
||||
// Show page.
|
||||
await ViewService.PushAsync(ViewTypes.BikesAtStation);
|
||||
|
@ -768,7 +743,6 @@ namespace TINK.ViewModel.Map
|
|||
$"{AppResources.ErrorPageNotLoaded}\r\n {exception.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -168,10 +168,7 @@ namespace TINK.ViewModel
|
|||
{ typeof(Services.BluetoothLock.BLE.LockItByScanServiceEventBased).FullName, "Live - Scan" },
|
||||
{ typeof(Services.BluetoothLock.BLE.LockItByScanServicePolling).FullName, "Live - Scan (Polling)" },
|
||||
{ typeof(Services.BluetoothLock.BLE.LockItByGuidService).FullName, "Live - Guid" },
|
||||
/* { typeof(Services.BluetoothLock.Arendi.LockItByGuidService).FullName, "Live - Guid (Arendi)" },
|
||||
{ typeof(Services.BluetoothLock.Arendi.LockItByScanService).FullName, "Live - Scan (Arendi)" },
|
||||
{ typeof(Services.BluetoothLock.Bluetoothle.LockItByGuidService).FullName, "Live - Guid (Ritchie)" }, */
|
||||
},
|
||||
},
|
||||
TinkApp.LocksServices.Active.GetType().FullName));
|
||||
|
||||
GeolocationServices = new ServicesViewModel(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue