2022-09-16 11:19:46 +02:00
using System ;
2021-05-13 20:03:07 +02:00
using System.Threading.Tasks ;
using Serilog ;
2022-08-30 15:42:25 +02:00
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock ;
using TINK.Model.Connector ;
using TINK.Model.Device ;
using TINK.Model.User ;
using TINK.MultilingualResources ;
2021-06-26 20:57:55 +02:00
using TINK.Repository.Exception ;
2022-08-30 15:42:25 +02:00
using TINK.Services.BluetoothLock ;
2021-05-13 20:03:07 +02:00
using TINK.Services.BluetoothLock.Exception ;
2022-08-30 15:42:25 +02:00
using TINK.Services.Geolocation ;
using TINK.View ;
2023-08-31 12:20:06 +02:00
using static TINK . Model . Bikes . BikeInfoNS . BluetoothLock . Command . GetLockedLocationCommand ;
using static TINK . ViewModel . Bikes . Bike . BluetoothLock . RequestHandlerFactory ;
2021-05-13 20:03:07 +02:00
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
2023-08-31 12:20:06 +02:00
public class BookedClosed : Base , IRequestHandler , IGetLockedLocationCommandListener
2022-09-06 16:08:19 +02:00
{
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
public BookedClosed (
IBikeInfoMutable selectedBike ,
Func < bool > isConnectedDelegate ,
Func < bool , IConnector > connectorFactory ,
2023-04-05 15:02:10 +02:00
IGeolocationService geolocation ,
2022-09-06 16:08:19 +02:00
ILocksService lockService ,
Func < IPollingUpdateTaskManager > viewUpdateManager ,
ISmartDevice smartDevice ,
IViewService viewService ,
IBikesViewModel bikesViewModel ,
IUser activeUser ) : base (
selectedBike ,
AppResources . ActionReturn , // Copri button text "Miete beenden"
2022-10-03 17:55:10 +02:00
true , // Show button to enabled returning of bike.
2022-09-06 16:08:19 +02:00
isConnectedDelegate ,
connectorFactory ,
geolocation ,
lockService ,
viewUpdateManager ,
smartDevice ,
viewService ,
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.
2023-08-31 12:20:06 +02:00
_endRentalActionViewModel = new EndRentalActionViewModel < BookedClosed > (
selectedBike ,
isConnectedDelegate ,
connectorFactory ,
lockService ,
viewUpdateManager ,
viewService ,
bikesViewModel ) ;
}
2022-09-06 16:08:19 +02:00
2023-08-31 12:20:06 +02:00
/// <summary>
/// Holds the view model for end rental action.
/// </summary>
private EndRentalActionViewModel < BookedClosed > _endRentalActionViewModel ;
2022-09-06 16:08:19 +02:00
/// <summary> Return bike. </summary>
2023-08-31 12:20:06 +02:00
public async Task < IRequestHandler > HandleRequestOption1 ( )
2022-09-06 16:08:19 +02:00
{
2023-08-31 12:20:06 +02:00
await _endRentalActionViewModel . EndRentalAsync ( ) ;
return Create (
SelectedBike ,
IsConnectedDelegate ,
ConnectorFactory ,
GeolocationService ,
LockService ,
ViewUpdateManager ,
SmartDevice ,
ViewService ,
BikesViewModel ,
ActiveUser ) ;
}
2022-09-06 16:08:19 +02:00
2023-08-31 12:20:06 +02:00
/// <summary> Open bike and update COPRI lock state. </summary>
public async Task < IRequestHandler > HandleRequestOption2 ( ) = > await OpenLock ( ) ;
2022-09-06 16:08:19 +02:00
2023-08-31 12:20:06 +02:00
/// <summary>
/// Processes the get lock location progress.
/// </summary>
/// <param name="step">Current step to process.</param>
public void ReportStep ( Step step ) = > _endRentalActionViewModel . ReportStep ( step ) ;
2022-09-06 16:08:19 +02:00
2023-08-31 12:20:06 +02:00
/// <summary>
/// Processes the get lock location state.
/// </summary>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync ( State state , string details ) = > await _endRentalActionViewModel . ReportStateAsync ( state , details ) ;
2022-09-06 16:08:19 +02:00
/// <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 ) ;
// Stop polling before returning bike.
BikesViewModel . IsIdle = false ;
BikesViewModel . ActionText = AppResources . ActivityTextOneMomentPlease ;
2023-08-31 12:20:06 +02:00
await ViewUpdateManager ( ) . StopAsync ( ) ;
2022-09-06 16:08:19 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextOpeningLock ;
2022-09-20 13:51:55 +02:00
ILockService btLock ;
2022-09-06 16:08:19 +02:00
try
{
2022-09-20 13:51:55 +02:00
btLock = LockService [ SelectedBike . LockInfo . Id ] ;
SelectedBike . LockInfo . State = ( await btLock . OpenAsync ( ) ) ? . GetLockingState ( ) ? ? LockingState . UnknownDisconnected ;
2022-09-06 16:08:19 +02:00
}
catch ( Exception exception )
{
2022-09-20 13:51:55 +02:00
BikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
if ( exception is OutOfReachException )
{
Log . ForContext < BookedClosed > ( ) . Debug ( "Lock can not be opened. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorOpenLockTitle ,
2023-08-31 12:20:06 +02:00
AppResources . ErrorLockOutOfReach ,
2022-09-06 16:08:19 +02:00
AppResources . MessageAnswerOk ) ;
}
else if ( exception is CouldntOpenBoldIsBlockedException )
{
Log . ForContext < BookedClosed > ( ) . Debug ( "Lock can not be opened. Bold is blocked. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorOpenLockTitle ,
2023-08-31 12:20:06 +02:00
AppResources . ErrorOpenLockBoldBlocked ,
2022-09-06 16:08:19 +02:00
AppResources . MessageAnswerOk ) ;
}
2023-02-22 14:03:35 +01:00
else if ( exception is CouldntOpenBoldStatusIsUnknownException )
2022-09-06 16:08:19 +02:00
{
2023-02-22 14:03:35 +01:00
Log . ForContext < BookedClosed > ( ) . Debug ( "Lock can not be opened. Bold status is unknown. {Exception}" , exception ) ;
2022-09-06 16:08:19 +02:00
await ViewService . DisplayAlert (
2023-08-31 12:20:06 +02:00
AppResources . ErrorOpenLockTitle ,
AppResources . ErrorOpenLockStatusUnknown ,
AppResources . MessageAnswerOk ) ;
2022-09-06 16:08:19 +02:00
}
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 ) ;
await ViewService . DisplayAlert (
AppResources . ErrorOpenLockTitle ,
2023-08-31 12:20:06 +02:00
AppResources . ErrorOpenLockStillClosed ,
AppResources . MessageAnswerOk ) ;
2022-09-06 16:08:19 +02:00
}
else
{
Log . ForContext < BookedClosed > ( ) . Error ( "Lock can not be opened. {Exception}" , exception ) ;
2023-08-31 12:20:06 +02:00
await ViewService . DisplayAdvancedAlert (
2022-09-06 16:08:19 +02:00
AppResources . ErrorOpenLockTitle ,
exception . Message ,
2023-08-31 12:20:06 +02:00
AppResources . ErrorTryAgain ,
AppResources . MessageAnswerOk ) ;
2022-09-06 16:08:19 +02:00
}
2023-04-19 12:14:14 +02:00
// When bold is blocked lock is still closed even if exception occurs.
2022-09-06 16:08:19 +02:00
// In all other cases state is supposed to be unknown. Example: Lock is out of reach and no more bluetooth connected.
SelectedBike . LockInfo . State = exception is StateAwareException stateAwareException
? stateAwareException . State
: LockingState . UnknownDisconnected ;
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
2023-08-31 12:20:06 +02:00
await ViewUpdateManager ( ) . StartAsync ( ) ;
2022-09-06 16:08:19 +02:00
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
2023-08-31 12:20:06 +02:00
BikesViewModel . RentalProcess . State = CurrentRentalProcess . None ;
2023-04-05 15:02:10 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , GeolocationService , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2022-09-06 16:08:19 +02:00
}
BikesViewModel . ActionText = AppResources . ActivityTextReadingChargingLevel ;
try
{
2022-09-20 13:51:55 +02:00
SelectedBike . LockInfo . BatteryPercentage = await btLock . GetBatteryPercentageAsync ( ) ;
2022-09-06 16:08:19 +02:00
}
catch ( Exception exception )
{
if ( exception is OutOfReachException )
{
2023-04-19 12:14:14 +02:00
Log . ForContext < BookedClosed > ( ) . Debug ( "Battery state can not be read, bike out of range. {Exception}" , exception ) ;
2022-09-06 16:08:19 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextErrorReadingChargingLevelOutOfReach ;
}
else
{
2023-04-19 12:14:14 +02:00
Log . ForContext < BookedClosed > ( ) . Error ( "Battery state can not be read. {Exception}" , exception ) ;
2022-09-06 16:08:19 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextErrorReadingChargingLevelGeneral ;
}
}
// Lock list to avoid multiple taps while copri action is pending.
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdatingLockingState ;
2022-09-20 13:51:55 +02:00
var versionTdo = btLock . VersionInfo ;
if ( versionTdo ! = null )
{
SelectedBike . LockInfo . VersionInfo = new VersionInfo . Builder
{
FirmwareVersion = versionTdo . FirmwareVersion ,
HardwareVersion = versionTdo . HardwareVersion ,
LockVersion = versionTdo . LockVersion ,
} . Build ( ) ;
}
2022-09-06 16:08:19 +02:00
IsConnected = IsConnectedDelegate ( ) ;
try
{
await ConnectorFactory ( IsConnected ) . Command . UpdateLockingStateAsync ( SelectedBike ) ;
}
catch ( Exception exception )
{
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 ) ;
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 ) ;
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 ) ;
BikesViewModel . ActionText = AppResources . ActivityTextErrorConnectionUpdateingLockstate ;
}
}
Log . ForContext < BookedClosed > ( ) . Information ( "User paused ride using {bike} successfully." , SelectedBike ) ;
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
2023-08-31 12:20:06 +02:00
await ViewUpdateManager ( ) . StartAsync ( ) ;
2022-09-06 16:08:19 +02:00
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
2023-08-31 12:20:06 +02:00
BikesViewModel . RentalProcess . State = CurrentRentalProcess . None ;
2023-04-05 15:02:10 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , GeolocationService , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2022-09-06 16:08:19 +02:00
}
}
2021-05-13 20:03:07 +02:00
}