2021-05-13 20:03:07 +02:00
using System ;
using System.Threading.Tasks ;
using TINK.Model.Connector ;
using TINK.Model.Bike.BluetoothLock ;
using TINK.Model.State ;
using TINK.View ;
2022-04-10 17:38:34 +02:00
using TINK.Services.Geolocation ;
2021-05-13 20:03:07 +02:00
using TINK.Services.BluetoothLock ;
using Serilog ;
2021-06-26 20:57:55 +02:00
using TINK.Repository.Exception ;
2021-05-13 20:03:07 +02:00
using TINK.Services.BluetoothLock.Exception ;
using Xamarin.Essentials ;
using TINK.MultilingualResources ;
using TINK.Model.Bikes.Bike.BluetoothLock ;
using TINK.Model.User ;
2021-06-26 20:57:55 +02:00
using TINK.Repository.Request ;
using TINK.Model.Device ;
2021-08-28 10:04:10 +02:00
using System.Collections.Generic ;
using System.Threading ;
2022-01-04 18:54:03 +01:00
using TINK.Model ;
2021-05-13 20:03:07 +02:00
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class BookedOpen : Base , IRequestHandler
{
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
public BookedOpen (
IBikeInfoMutable selectedBike ,
Func < bool > isConnectedDelegate ,
Func < bool , IConnector > connectorFactory ,
IGeolocation geolocation ,
ILocksService lockService ,
Func < IPollingUpdateTaskManager > viewUpdateManager ,
2021-06-26 20:57:55 +02:00
ISmartDevice smartDevice ,
2021-05-13 20:03:07 +02:00
IViewService viewService ,
IBikesViewModel bikesViewModel ,
IUser activeUser ) : base (
selectedBike ,
AppResources . ActionCloseAndReturn , // Copri button text: "Schloss schließen & Miete beenden"
true , // Show button to allow user to return bike.
isConnectedDelegate ,
connectorFactory ,
geolocation ,
lockService ,
2021-06-26 20:57:55 +02:00
viewUpdateManager ,
smartDevice ,
2021-05-13 20:03:07 +02:00
viewService ,
bikesViewModel ,
activeUser )
{
LockitButtonText = AppResources . ActionClose ; // BT button text "Schließen".
IsLockitButtonVisible = true ; // Show button to allow user to lock bike.
}
/// <summary> Gets the bike state. </summary>
public override InUseStateEnum State = > InUseStateEnum . Disposable ;
/// <summary> Close lock and return bike.</summary>
2021-06-26 20:57:55 +02:00
public async Task < IRequestHandler > HandleRequestOption1 ( ) = > await CloseLockAndReturnBike ( ) ;
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
public async Task < IRequestHandler > HandleRequestOption2 ( ) = > await CloseLock ( ) ;
/// <summary> Close lock and return bike.</summary>
public async Task < IRequestHandler > CloseLockAndReturnBike ( )
2021-05-13 20:03:07 +02:00
{
2021-08-28 10:04:10 +02:00
// Prevent concurrent interaction
2021-05-13 20:03:07 +02:00
BikesViewModel . IsIdle = false ;
2021-08-28 10:04:10 +02:00
// Start getting geolocation.
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocationStart ;
var ctsLocation = new CancellationTokenSource ( ) ;
Task < Location > currentLocationTask = null ;
var timeStamp = DateTime . Now ;
try
{
currentLocationTask = Geolocation . GetAsync ( ctsLocation . Token , timeStamp ) ;
}
catch ( Exception ex )
{
// No location information available.
Log . ForContext < BookedOpen > ( ) . Information ( "Returning bike {Bike} is not possible. Start query location failed. {Exception}" , SelectedBike , ex ) ;
BikesViewModel . ActionText = string . Empty ;
await ViewService . DisplayAlert (
AppResources . MessageErrorQueryLocationStartTitle ,
$"{AppResources.MessageErrorQueryLocationMessage}\r\n{ex.Message}" ,
AppResources . MessageAnswerOk ) ;
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ; // Restart polling again.
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ; // Unlock GUI
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
}
// Ask whether to really return bike?
2021-05-13 20:03:07 +02:00
var l_oResult = await ViewService . DisplayAlert (
string . Empty ,
2021-08-28 10:04:10 +02:00
string . Format ( AppResources . QuestionCloseLockAndReturnBike , SelectedBike . GetFullDisplayName ( ) ) ,
AppResources . MessageAnswerYes ,
AppResources . MessageAnswerNo ) ;
2021-05-13 20:03:07 +02:00
if ( l_oResult = = false )
{
// User aborted closing and returning bike process
Log . ForContext < BookedOpen > ( ) . Information ( "User selected booked bike {l_oId} in order to close and return but action was canceled." , SelectedBike . Id ) ;
2021-08-28 10:04:10 +02:00
// Cancel getting geolocation.
ctsLocation . Cancel ( ) ;
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocationCancelWait ;
try
{
await Task . WhenAny ( new List < Task > { currentLocationTask ? ? Task . CompletedTask } ) ;
}
catch ( Exception ex )
{
// No location information available.
Log . ForContext < BookedOpen > ( ) . Information ( "Canceling query location failed on abort returning opened bike failed. {Exception}" , SelectedBike , ex ) ;
}
2021-05-13 20:03:07 +02:00
BikesViewModel . IsIdle = true ;
return this ;
}
// Unlock bike.
Log . ForContext < BookedOpen > ( ) . Information ( "Request to return bike {bike} detected." , SelectedBike ) ;
// Stop polling before returning bike.
BikesViewModel . ActionText = AppResources . ActivityTextOneMomentPlease ;
await ViewUpdateManager ( ) . StopUpdatePeridically ( ) ;
2022-04-10 17:38:34 +02:00
// Notify COPRI about start reaturning bike
BikesViewModel . ActionText = AppResources . ActivityTextStartReturningBike ;
IsConnected = IsConnectedDelegate ( ) ;
try
{
await ConnectorFactory ( IsConnected ) . Command . StartReturningBike (
SelectedBike ) ;
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
if ( exception is WebConnectFailureException )
{
// Copri server is not reachable.
Log . ForContext < BookedOpen > ( ) . Information ( "User selected booked bike {bike} but returing failed (Copri server not reachable)." , SelectedBike ) ;
await ViewService . DisplayAdvancedAlert (
AppResources . ErrorReturnBikeNoWebTitle ,
string . Format ( "{0}\r\n{1}" , AppResources . ErrorReturnBikeNoWebMessage , WebConnectFailureException . GetHintToPossibleExceptionsReasons ) ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
}
else
{
Log . ForContext < BookedOpen > ( ) . Error ( "User selected booked bike {bike} but returning failed. {@l_oException}" , SelectedBike . Id , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorReturnBikeTitle ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
}
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
}
2021-08-28 10:04:10 +02:00
// Close lock
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextClosingLock ;
try
{
2022-04-10 17:38:34 +02:00
SelectedBike . LockInfo . State = ( await LockService [ SelectedBike . LockInfo . Id ] . CloseAsync ( ) ) ? . GetLockingState ( ) ? ? LockingState . UnknownDisconnected ;
2021-05-13 20:03:07 +02:00
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
2022-04-10 17:38:34 +02:00
SelectedBike . LockInfo . State = exception is StateAwareException stateAwareException
? stateAwareException . State
: LockingState . UnknownDisconnected ;
2021-08-28 10:04:10 +02:00
// Signal cts to cancel getting geolocation.
ctsLocation . Cancel ( ) ;
2022-04-10 17:38:34 +02:00
Task updateLockingStateTask = Task . CompletedTask ;
try
{
updateLockingStateTask = ConnectorFactory ( IsConnected ) . Command . UpdateLockingStateAsync (
SelectedBike ) ;
}
catch ( Exception innerExceptionStartUpdateLockingState )
{
// No location information available/ updating state failed.
Log . ForContext < BookedOpen > ( ) . Information ( "Start update locking state failed on lock operating error. {Exception}" , SelectedBike , innerExceptionStartUpdateLockingState ) ;
}
2021-08-28 10:04:10 +02:00
2021-05-13 20:03:07 +02:00
if ( exception is OutOfReachException )
{
Log . ForContext < BookedOpen > ( ) . Debug ( "Lock can not be closed. Lock is out of reach. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
AppResources . ErrorCloseLockOutOfReachMessage ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else if ( exception is CounldntCloseMovingException )
{
Log . ForContext < BookedOpen > ( ) . Debug ( "Lock can not be closed. Lock is out of reach. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
AppResources . ErrorCloseLockMovingMessage ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else if ( exception is CouldntCloseBoldBlockedException )
{
Log . ForContext < BookedOpen > ( ) . Debug ( "Lock can not be closed. Lock is out of reach. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
AppResources . ErrorCloseLockBoldBlockedMessage ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else
{
Log . ForContext < BookedOpen > ( ) . Error ( "Lock can not be closed. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
exception . Message ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
2021-08-28 10:04:10 +02:00
// Wait until cancel getting geolocation has completed.
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocationCancelWait ;
try
{
2022-04-10 17:38:34 +02:00
await Task . WhenAll ( new List < Task > { currentLocationTask ? ? Task . CompletedTask , updateLockingStateTask } ) ;
2021-08-28 10:04:10 +02:00
}
2022-04-10 17:38:34 +02:00
catch ( Exception innerExWhenAll )
2021-08-28 10:04:10 +02:00
{
2022-04-10 17:38:34 +02:00
// No location information available/ updating state failed.
Log . ForContext < BookedOpen > ( ) . Information ( "Canceling query location/ updating lock state failed on closing lock error. {Exception}" , SelectedBike , innerExWhenAll ) ;
2021-08-28 10:04:10 +02:00
}
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ; // Restart polling again.
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ; // Unlock GUI
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
if ( SelectedBike . LockInfo . State ! = LockingState . Closed )
{
Log . ForContext < BookedOpen > ( ) . Error ( $"Lock can not be closed. Invalid locking state state {SelectedBike.LockInfo.State} detected." ) ;
2022-04-10 17:38:34 +02:00
BikesViewModel . ActionText = string . Empty ;
2021-08-28 10:04:10 +02:00
// Signal cts to cancel getting geolocation.
ctsLocation . Cancel ( ) ;
2022-04-10 17:38:34 +02:00
Task updateLockingStateTask = Task . CompletedTask ;
try
{
updateLockingStateTask = ConnectorFactory ( IsConnected ) . Command . UpdateLockingStateAsync (
SelectedBike ) ;
}
catch ( Exception innerExceptionStartUpdateLockingState )
{
// No location information available/ updating state failed.
Log . ForContext < BookedOpen > ( ) . Information ( "Start update locking state failed on unexpected state. {Exception}" , SelectedBike , innerExceptionStartUpdateLockingState ) ;
}
2021-05-13 20:03:07 +02:00
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
SelectedBike . LockInfo . State = = LockingState . Open
? AppResources . ErrorCloseLockStillOpenMessage
2021-08-28 10:04:10 +02:00
: string . Format ( AppResources . ErrorCloseLockUnexpectedStateMessage , SelectedBike . LockInfo . State ) ,
AppResources . MessageAnswerOk ) ;
// Wait until cancel getting geolocation has completed.
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocationCancelWait ;
try
{
2022-04-10 17:38:34 +02:00
await Task . WhenAll ( new List < Task > { currentLocationTask ? ? Task . CompletedTask , updateLockingStateTask } ) ;
2021-08-28 10:04:10 +02:00
}
2022-04-10 17:38:34 +02:00
catch ( Exception innerExWhenAll )
2021-08-28 10:04:10 +02:00
{
// No location information available.
2022-04-10 17:38:34 +02:00
Log . ForContext < BookedOpen > ( ) . Information ( "Canceling query location/ updating lock state failed failed on unexpected lock state failed. {Exception}" , SelectedBike , innerExWhenAll ) ;
2021-08-28 10:04:10 +02:00
}
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ; // Restart polling again.
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ; // Unlock GUI
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
2021-08-28 10:04:10 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocation ;
Location currentLocation = null ;
2021-05-13 20:03:07 +02:00
try
{
2021-08-28 10:04:10 +02:00
var task = await Task . WhenAny ( new List < Task > { currentLocationTask } ) ;
currentLocation = currentLocationTask . Result ;
2021-05-13 20:03:07 +02:00
}
catch ( Exception ex )
{
// No location information available.
2021-08-28 10:04:10 +02:00
Log . ForContext < BookedOpen > ( ) . Information ( "Returning bike {Bike} is not possible. Query location failed. {Exception}" , SelectedBike , ex ) ;
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = string . Empty ;
2021-08-28 10:04:10 +02:00
await ViewService . DisplayAdvancedAlert (
AppResources . MessageErrorQueryLocationTitle ,
AppResources . MessageErrorQueryLocationMessage ,
ex . GetErrorMessage ( ) ,
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ; // Restart polling again.
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ; // Unlock GUI
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
// Lock list to avoid multiple taps while copri action is pending.
2021-08-28 10:04:10 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextReturningBike ;
2021-05-13 20:03:07 +02:00
IsConnected = IsConnectedDelegate ( ) ;
var feedBackUri = SelectedBike ? . OperatorUri ;
2022-01-04 18:54:03 +01:00
BookingFinishedModel bookingFinished ;
2021-05-13 20:03:07 +02:00
try
{
2022-01-04 18:54:03 +01:00
bookingFinished = await ConnectorFactory ( IsConnected ) . Command . DoReturn (
2021-05-13 20:03:07 +02:00
SelectedBike ,
currentLocation ! = null
? new LocationDto . Builder
{
Latitude = currentLocation . Latitude ,
Longitude = currentLocation . Longitude ,
Accuracy = currentLocation . Accuracy ? ? double . NaN ,
Age = timeStamp . Subtract ( currentLocation . Timestamp . DateTime ) ,
} . Build ( )
2021-06-26 20:57:55 +02:00
: null ,
SmartDevice ) ;
2021-05-13 20:03:07 +02:00
// If canceling bike succedes remove bike because it is not ready to be booked again
IsRemoveBikeRequired = true ;
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
if ( exception is WebConnectFailureException )
{
// Copri server is not reachable.
Log . ForContext < BookedOpen > ( ) . Information ( "User selected booked bike {bike} but returing failed (Copri server not reachable)." , SelectedBike ) ;
2021-08-28 10:04:10 +02:00
await ViewService . DisplayAdvancedAlert (
AppResources . ErrorReturnBikeNoWebTitle ,
string . Format ( "{0}\r\n{1}" , AppResources . ErrorReturnBikeNoWebMessage , WebConnectFailureException . GetHintToPossibleExceptionsReasons ) ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else if ( exception is NotAtStationException notAtStationException )
{
// COPRI returned an error.
Log . ForContext < BookedOpen > ( ) . Information ( "User selected booked bike {bike} but returing failed. COPRI returned an error." , SelectedBike ) ;
await ViewService . DisplayAlert (
2022-04-10 17:38:34 +02:00
AppResources . ErrorReturnBikeTitle ,
2021-05-13 20:03:07 +02:00
string . Format ( AppResources . ErrorReturnBikeNotAtStationMessage , notAtStationException . StationNr , notAtStationException . Distance ) ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else if ( exception is NoGPSDataException )
{
// COPRI returned an error.
Log . ForContext < BookedOpen > ( ) . Information ( "User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error." , SelectedBike ) ;
await ViewService . DisplayAlert (
2022-04-10 17:38:34 +02:00
AppResources . ErrorReturnBikeTitle ,
2021-05-13 20:03:07 +02:00
string . Format ( AppResources . ErrorReturnBikeLockOpenNoGPSMessage ) ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else if ( exception is ResponseException copriException )
{
// Copri server is not reachable.
Log . ForContext < BookedOpen > ( ) . Information ( "User selected booked bike {bike} but returing failed. COPRI returned an error." , SelectedBike ) ;
await ViewService . DisplayAdvancedAlert (
"Statusfehler beim Zurückgeben des Rads!" ,
copriException . Message ,
copriException . Response ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else
{
2022-04-10 17:38:34 +02:00
Log . ForContext < BookedOpen > ( ) . Error ( "User selected booked bike {bike} but returning failed. {@l_oException}" , SelectedBike . Id , exception ) ;
2021-05-13 20:03:07 +02:00
2022-04-10 17:38:34 +02:00
await ViewService . DisplayAlert (
AppResources . ErrorReturnBikeTitle ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
Log . ForContext < BookedOpen > ( ) . Information ( "User returned bike {bike} successfully." , SelectedBike ) ;
// Disconnect lock.
BikesViewModel . ActionText = AppResources . ActivityTextDisconnectingLock ;
try
{
SelectedBike . LockInfo . State = await LockService . DisconnectAsync ( SelectedBike . LockInfo . Id , SelectedBike . LockInfo . Guid ) ;
}
catch ( Exception exception )
{
2021-08-28 10:04:10 +02:00
Log . ForContext < BookedOpen > ( ) . Error ( "Lock can not be disconnected. {Exception}" , exception ) ;
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextErrorDisconnect ;
}
#if ! USERFEEDBACKDLG_OFF
// Do get Feedback
2021-12-08 17:57:30 +01:00
var feedback = await ViewService . DisplayUserFeedbackPopup ( bookingFinished ? . Co2Saving ) ;
2021-05-13 20:03:07 +02:00
try
{
await ConnectorFactory ( IsConnected ) . Command . DoSubmitFeedback (
2021-06-26 20:57:55 +02:00
new UserFeedbackDto { BikeId = SelectedBike . Id , IsBikeBroken = feedback . IsBikeBroken , Message = feedback . Message } ,
2021-05-13 20:03:07 +02:00
feedBackUri ) ;
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
if ( exception is ResponseException copriException )
{
// Copri server is not reachable.
2021-08-01 17:24:15 +02:00
Log . ForContext < BookedOpen > ( ) . Information ( "Submitting feedback for bike {bike} failed. COPRI returned an error." , SelectedBike ) ;
2021-05-13 20:03:07 +02:00
}
else
{
2021-08-01 17:24:15 +02:00
Log . ForContext < BookedOpen > ( ) . Error ( "Submitting feedback for bike {bike} failed. {@l_oException}" , SelectedBike . Id , exception ) ;
2021-05-13 20:03:07 +02:00
}
2021-06-26 20:57:55 +02:00
await ViewService . DisplayAlert (
AppResources . ErrorReturnSubmitFeedbackTitle ,
AppResources . ErrorReturnSubmitFeedbackMessage ,
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
#endif
2022-01-04 18:54:03 +01:00
if ( bookingFinished ! = null & & bookingFinished . MiniSurvey . Questions . Count > 0 )
2021-08-01 17:24:15 +02:00
{
await ViewService . PushModalAsync ( ViewTypes . MiniSurvey ) ;
}
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
2021-06-26 20:57:55 +02:00
public async Task < IRequestHandler > CloseLock ( )
2021-05-13 20:03:07 +02:00
{
// Unlock bike.
BikesViewModel . IsIdle = false ;
Log . ForContext < BookedOpen > ( ) . Information ( "User request to lock bike {bike} in order to pause ride." , SelectedBike ) ;
2021-08-28 10:04:10 +02:00
// Start getting geolocation.
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocationStart ;
var ctsLocation = new CancellationTokenSource ( ) ;
Task < Location > currentLocationTask = null ;
var timeStamp = DateTime . Now ;
try
{
currentLocationTask = Geolocation . GetAsync ( ctsLocation . Token , timeStamp ) ;
}
catch ( Exception ex )
{
// No location information available.
Log . ForContext < BookedOpen > ( ) . Information ( "Closing lock of bike {Bike} is not possible. Starting query location failed. {Exception}" , SelectedBike , ex ) ;
BikesViewModel . ActionText = AppResources . ActivityTextErrorQueryLocationQuery ;
}
2021-05-13 20:03:07 +02:00
// Stop polling before returning bike.
BikesViewModel . ActionText = AppResources . ActivityTextOneMomentPlease ;
await ViewUpdateManager ( ) . StopUpdatePeridically ( ) ;
2021-08-28 10:04:10 +02:00
// Close lock
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextClosingLock ;
try
{
2022-04-10 17:38:34 +02:00
SelectedBike . LockInfo . State = ( await LockService [ SelectedBike . LockInfo . Id ] . CloseAsync ( ) ) ? . GetLockingState ( ) ? ? LockingState . UnknownDisconnected ;
2021-05-13 20:03:07 +02:00
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
2021-08-28 10:04:10 +02:00
// Signal cts to cancel getting geolocation.
ctsLocation . Cancel ( ) ;
2021-05-13 20:03:07 +02:00
if ( exception is OutOfReachException )
{
Log . ForContext < BookedOpen > ( ) . Debug ( "Lock can not be closed. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
AppResources . ErrorCloseLockOutOfReachMessage ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else if ( exception is CounldntCloseMovingException )
{
Log . ForContext < BookedOpen > ( ) . Debug ( "Lock can not be closed. Lock is out of reach. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
AppResources . ErrorCloseLockMovingMessage ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else if ( exception is CouldntCloseBoldBlockedException )
{
Log . ForContext < BookedOpen > ( ) . Debug ( "Lock can not be closed. Lock is out of reach. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
AppResources . ErrorCloseLockBoldBlockedMessage ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else
{
Log . ForContext < BookedOpen > ( ) . Error ( "Lock can not be closed. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorCloseLockTitle ,
exception . Message ,
2021-08-28 10:04:10 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
SelectedBike . LockInfo . State = exception is StateAwareException stateAwareException
? stateAwareException . State
2022-04-10 17:38:34 +02:00
: LockingState . UnknownDisconnected ;
2021-05-13 20:03:07 +02:00
2021-08-28 10:04:10 +02:00
// Wait until cancel getting geolocation has completed.
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocationCancelWait ;
try
{
await Task . WhenAny ( new List < Task > { currentLocationTask ? ? Task . CompletedTask } ) ;
}
catch ( Exception ex )
{
// No location information available.
Log . ForContext < BookedOpen > ( ) . Information ( "Canceling query location failed on closing lock error. {Exception}" , SelectedBike , ex ) ;
}
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
2021-08-28 10:04:10 +02:00
2021-05-13 20:03:07 +02:00
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
// Get geoposition.
2021-08-28 10:04:10 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextQueryLocation ;
2021-05-13 20:03:07 +02:00
Location currentLocation = null ;
try
{
2021-08-28 10:04:10 +02:00
await Task . WhenAny ( new List < Task > { currentLocationTask } ) ;
currentLocation = currentLocationTask . Result ;
2021-05-13 20:03:07 +02:00
}
catch ( Exception ex )
{
// No location information available.
2021-08-28 10:04:10 +02:00
Log . ForContext < BookedOpen > ( ) . Information ( "Getting geolocation when closing lock of bike {Bike} failed. {Exception}" , SelectedBike , ex ) ;
2021-05-13 20:03:07 +02:00
2021-08-28 10:04:10 +02:00
BikesViewModel . ActionText = AppResources . ActivityTextErrorQueryLocationWhenAny ;
2021-05-13 20:03:07 +02:00
}
// Lock list to avoid multiple taps while copri action is pending.
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdatingLockingState ;
IsConnected = IsConnectedDelegate ( ) ;
try
{
await ConnectorFactory ( IsConnected ) . Command . UpdateLockingStateAsync (
SelectedBike ,
currentLocation ! = null
? new LocationDto . Builder
{
Latitude = currentLocation . Latitude ,
Longitude = currentLocation . Longitude ,
Accuracy = currentLocation . Accuracy ? ? double . NaN ,
Age = timeStamp . Subtract ( currentLocation . Timestamp . DateTime ) ,
} . Build ( )
: null ) ;
}
catch ( Exception exception )
{
if ( exception is WebConnectFailureException )
{
// Copri server is not reachable.
Log . ForContext < BookedOpen > ( ) . Information ( "User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable)." , SelectedBike ) ;
BikesViewModel . ActionText = AppResources . ActivityTextErrorNoWebUpdateingLockstate ;
}
else if ( exception is ResponseException copriException )
{
// Copri server is not reachable.
Log . ForContext < BookedOpen > ( ) . Information ( "User locked bike {bike} in order to pause ride but updating failed. Message: {Message} Details: {Details}" , SelectedBike , copriException . Message , copriException . Response ) ;
BikesViewModel . ActionText = AppResources . ActivityTextErrorStatusUpdateingLockstate ;
}
else
{
Log . ForContext < BookedOpen > ( ) . Error ( "User locked bike {bike} in order to pause ride but updating failed. {@l_oException}" , SelectedBike . Id , exception ) ;
BikesViewModel . ActionText = AppResources . ActivityTextErrorConnectionUpdateingLockstate ;
}
}
Log . ForContext < BookedOpen > ( ) . Information ( "User paused ride using {bike} successfully." , SelectedBike ) ;
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
2021-06-26 20:57:55 +02:00
return RequestHandlerFactory . Create ( SelectedBike , IsConnectedDelegate , ConnectorFactory , Geolocation , LockService , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
2021-05-13 20:03:07 +02:00
}
}
}