2022-08-30 15:42:25 +02:00
using System ;
2021-05-13 20:03:07 +02:00
using System.Threading.Tasks ;
2022-08-30 15:42:25 +02:00
using Serilog ;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock ;
2021-05-13 20:03:07 +02:00
using TINK.Model.Connector ;
2022-08-30 15:42:25 +02:00
using TINK.Model.Device ;
using TINK.Model.User ;
using TINK.MultilingualResources ;
2021-06-26 20:57:55 +02:00
using TINK.Repository.Exception ;
2021-05-13 20:03:07 +02:00
using TINK.Services.BluetoothLock ;
using TINK.Services.BluetoothLock.Exception ;
using TINK.Services.BluetoothLock.Tdo ;
2022-04-10 17:38:34 +02:00
using TINK.Services.Geolocation ;
2021-05-13 20:03:07 +02:00
using TINK.View ;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class BookedDisconnected : Base , IRequestHandler
{
2021-06-26 20:57:55 +02:00
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
2021-05-13 20:03:07 +02:00
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
public BookedDisconnected (
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 ,
2022-08-30 15:42:25 +02:00
nameof ( BookedDisconnected ) ,
false ,
2021-05-13 20:03:07 +02:00
isConnectedDelegate ,
connectorFactory ,
geolocation ,
lockService ,
viewUpdateManager ,
2021-06-26 20:57:55 +02:00
smartDevice ,
2021-05-13 20:03:07 +02:00
viewService ,
bikesViewModel ,
activeUser )
{
LockitButtonText = AppResources . ActionSearchLock ;
2022-08-30 15:42:25 +02:00
IsLockitButtonVisible = true ;
2021-05-13 20:03:07 +02:00
}
2021-06-26 20:57:55 +02:00
public async Task < IRequestHandler > HandleRequestOption1 ( ) = > await UnsupportedRequest ( ) ;
/// <summary> Scan for lock.</summary>
/// <returns></returns>
public async Task < IRequestHandler > HandleRequestOption2 ( ) = > await ConnectLock ( ) ;
2022-08-30 15:42:25 +02:00
/// <summary> Requst is not supported, button should be disabled. </summary>
/// <returns></returns>
public async Task < IRequestHandler > UnsupportedRequest ( )
2021-05-13 20:03:07 +02:00
{
2021-06-26 20:57:55 +02:00
Log . ForContext < BookedDisconnected > ( ) . Error ( "Click of unsupported button click detected." ) ;
return await Task . FromResult < IRequestHandler > ( this ) ;
2021-05-13 20:03:07 +02:00
}
/// <summary> Scan for lock.</summary>
/// <returns></returns>
2021-06-26 20:57:55 +02:00
public async Task < IRequestHandler > ConnectLock ( )
2021-05-13 20:03:07 +02:00
{
// Lock list to avoid multiple taps while copri action is pending.
BikesViewModel . IsIdle = false ;
Log . ForContext < BookedDisconnected > ( ) . Information ( "Request to search {bike} detected." , SelectedBike ) ;
// Stop polling before getting new auth-values.
BikesViewModel . ActionText = AppResources . ActivityTextOneMomentPlease ;
await ViewUpdateManager ( ) . StopUpdatePeridically ( ) ;
BikesViewModel . ActionText = AppResources . ActivityTextQuerryServer ;
IsConnected = IsConnectedDelegate ( ) ;
try
{
// Repeat booking to get a new seed/ k_user value.
await ConnectorFactory ( IsConnected ) . Command . CalculateAuthKeys ( SelectedBike ) ;
}
2022-04-10 17:38:34 +02:00
catch ( Exception exception )
2021-05-13 20:03:07 +02:00
{
BikesViewModel . ActionText = string . Empty ;
2022-04-10 17:38:34 +02:00
if ( exception is WebConnectFailureException )
2021-05-13 20:03:07 +02:00
{
// Copri server is not reachable.
2021-08-28 10:04:10 +02:00
Log . ForContext < BookedDisconnected > ( ) . Information ( "User selected booked bike {l_oId} to connect to lock. (Copri server not reachable)." , SelectedBike . Id ) ;
2021-05-13 20:03:07 +02:00
await ViewService . DisplayAlert (
2022-04-10 17:38:34 +02:00
AppResources . MessageConnectLockErrorTitle ,
$"{AppResources.ErrorConnectLockRentedBikeNoWebMessage}\r\n{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" ,
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
else
{
2022-04-10 17:38:34 +02:00
Log . ForContext < BookedDisconnected > ( ) . Error ( "User selected booked bike {l_oId} to connect to lock. {@l_oException}" , SelectedBike . Id , exception ) ;
2021-05-13 20:03:07 +02:00
await ViewService . DisplayAlert (
2022-04-10 17:38:34 +02:00
AppResources . MessageConnectLockErrorTitle ,
$"{AppResources.ErrorConnectLockGeneralErrorMessage}\r\n{exception.Message}" ,
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
}
// Restart polling again.
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
2022-08-30 15:42:25 +02:00
BikesViewModel . ActionText = string . Empty ;
2021-05-13 20:03:07 +02:00
BikesViewModel . IsIdle = true ;
return this ;
}
LockInfoTdo result = null ;
var continueConnect = true ;
var retryCount = 1 ;
while ( continueConnect & & result = = null )
{
BikesViewModel . ActionText = AppResources . ActivityTextSearchingLock ;
try
{
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 ) ) ;
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
2022-04-10 17:38:34 +02:00
if ( exception is ConnectBluetoothNotOnException )
{
continueConnect = false ;
await ViewService . DisplayAlert (
AppResources . MessageConnectLockErrorTitle ,
AppResources . ErrorFindLockBluetoothNotOn ,
AppResources . MessageAnswerOk ) ;
}
else if ( exception is ConnectLocationPermissionMissingException )
{
continueConnect = false ;
await ViewService . DisplayAlert (
AppResources . MessageConnectLockErrorTitle ,
AppResources . ErrorFindLockLocationPermissionMissing ,
AppResources . MessageAnswerOk ) ;
}
else if ( exception is ConnectLocationOffException )
{
continueConnect = false ;
await ViewService . DisplayAlert (
AppResources . MessageConnectLockErrorTitle ,
AppResources . ErrorFindLockLocationOff ,
AppResources . MessageAnswerOk ) ;
}
else if ( exception is OutOfReachException )
2021-05-13 20:03:07 +02:00
{
Log . ForContext < BookedDisconnected > ( ) . Debug ( "Lock can not be found. {Exception}" , exception ) ;
continueConnect = await ViewService . DisplayAlert (
2022-04-10 17:38:34 +02:00
AppResources . MessageConnectLockErrorTitle ,
AppResources . ErrorFindLockRentedBikeOutOfReachMessage ,
AppResources . MessageAnswerRetry ,
AppResources . MessageAnswerCancel ) ;
2021-05-13 20:03:07 +02:00
}
else
{
Log . ForContext < BookedDisconnected > ( ) . Error ( "Lock can not be found. {Exception}" , exception ) ;
2022-04-10 17:38:34 +02:00
string message ;
if ( retryCount < 2 )
{
message = AppResources . ErrorBookedSearchMessage ;
2022-08-30 15:42:25 +02:00
}
2022-04-10 17:38:34 +02:00
else if ( retryCount < 3 )
{
message = AppResources . ErrorBookedSearchMessageEscalationLevel1 ;
}
else
{
message = AppResources . ErrorBookedSearchMessageEscalationLevel2 ;
}
2021-06-26 20:57:55 +02:00
continueConnect = await ViewService . DisplayAdvancedAlert (
2022-04-10 17:38:34 +02:00
AppResources . MessageConnectLockErrorTitle ,
message ,
"" , // bool IsReportLevelVerbose ? exception.Message : string.Empty, // or use ActiveUser.DebugLevel.HasFlag(Permissions.ReportLevel) instead?
AppResources . MessageAnswerRetry ,
AppResources . MessageAnswerCancel ) ;
2021-05-13 20:03:07 +02:00
}
if ( continueConnect )
{
retryCount + + ;
continue ;
}
// Quit and restart polling again.
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
return this ;
}
}
if ( result ? . State = = null )
{
Log . ForContext < BookedDisconnected > ( ) . Information ( "Lock for bike {bike} not found." , SelectedBike ) ;
2022-08-30 15:42:25 +02:00
BikesViewModel . ActionText = string . Empty ;
2021-05-13 20:03:07 +02:00
await ViewService . DisplayAlert (
2022-04-10 17:38:34 +02:00
AppResources . MessageConnectLockErrorTitle ,
2021-05-13 20:03:07 +02:00
$"Schlossstatus des gemieteten Rads konnte nicht ermittelt werden." ,
2022-04-10 17:38:34 +02:00
AppResources . MessageAnswerOk ) ;
2021-05-13 20:03:07 +02:00
// Restart polling again.
BikesViewModel . ActionText = AppResources . ActivityTextStartingUpdater ;
await ViewUpdateManager ( ) . StartUpdateAyncPeridically ( ) ;
BikesViewModel . ActionText = string . Empty ;
BikesViewModel . IsIdle = true ;
return this ;
}
var state = result . State . Value . 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}." ) ;
// Restart polling again.
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
}
}
}