Version 3.0.381

This commit is contained in:
Anja 2024-04-09 12:53:23 +02:00
parent f963c0a219
commit 3a363acf3a
1525 changed files with 60589 additions and 125098 deletions

View file

@ -0,0 +1,56 @@
using System;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable>
{
/// <summary>
/// Constructs the request handler base.
/// </summary>
/// <param name="selectedBike">Bike which is reserved or for which reservation is canceled.</param>
/// <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 Base(
Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable selectedBike,
string buttonText,
bool isCopriButtonVisible,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(selectedBike, buttonText, isCopriButtonVisible, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
{
GeolocationService = geolocation
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. Parameter {nameof(geolocation)} must not be null.");
LockService = lockService
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. Parameter {nameof(lockService)} must not be null.");
}
/// <summary>
/// Service to query geolocation information.
/// </summary>
protected IGeolocationService GeolocationService { get; }
/// <summary>
/// Service to control locks.
/// </summary>
protected ILocksService LockService { get; }
public string LockitButtonText { get; protected set; }
public bool IsLockitButtonVisible { get; protected set; }
public string ErrorText => string.Empty;
}
}

View file

@ -0,0 +1,134 @@
using System;
using System.Threading.Tasks;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using EndRentalCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.EndRentalCommand;
using OpenCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.OpenCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class BookedClosed : Base, IRequestHandler, EndRentalCommand.IEndRentalCommandListener, OpenCommand.IOpenCommandListener
{
/// <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,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionEndRental, // End Rental
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = AppResources.ActionOpenLock; // Open Lock
IsLockitButtonVisible = true; // Show button
_openLockActionViewModel = new OpenLockActionViewModel<BookedClosed>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
_endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
selectedBike,
isConnectedDelegate,
connectorFactory,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for end rental action.
/// </summary>
private readonly EndRentalActionViewModel<BookedClosed> _endRentalActionViewModel;
/// <summary>
/// Processes the get lock location progress.
/// </summary>
/// <param name="step">Current step to process.</param>
public void ReportStep(EndRentalCommand.Step step) => _endRentalActionViewModel.ReportStep(step);
/// <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(EndRentalCommand.State state, string details) => await _endRentalActionViewModel.ReportStateAsync(state, details);
/// <summary> End Rental. </summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _endRentalActionViewModel.EndRentalAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary>
/// Holds the view model for close action.
/// </summary>
private readonly OpenLockActionViewModel<BookedClosed> _openLockActionViewModel;
/// <summary>
/// Processes the open lock progress.
/// </summary>
/// <param name="step">Current step to process.</param>
public void ReportStep(OpenCommand.Step step) => _openLockActionViewModel.ReportStep(step);
/// <summary>
/// Processes the open lock state.
/// </summary>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(OpenCommand.State state, string details) => await _openLockActionViewModel.ReportStateAsync(state, details);
/// <summary> Open lock. </summary>
public async Task<IRequestHandler> HandleRequestOption2()
{
await _openLockActionViewModel.OpenLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
}
}

View file

@ -0,0 +1,109 @@
using System;
using System.Threading.Tasks;
using Serilog;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using ConnectCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.ConnectAndGetStateCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class BookedDisconnected : Base, IRequestHandler, ConnectCommand.IConnectAndGetStateCommandListener
{
/// <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 BookedDisconnected(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) :
base(
selectedBike,
AppResources.ActionSearchLock, // Connect Lock
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = GetType().Name;
IsLockitButtonVisible = false;
_connectLockActionViewModel = new ConnectLockActionViewModel<BookedDisconnected>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for connecting to lock action.
/// </summary>
private readonly ConnectLockActionViewModel<BookedDisconnected> _connectLockActionViewModel;
/// <summary>
/// Processes the connect to lock progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(ConnectCommand.Step step) => _connectLockActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the connect to lock state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(ConnectCommand.State state, string details) => await _connectLockActionViewModel.ReportStateAsync(state, details);
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
/// <summary> Scan for lock.</summary>
/// <returns></returns>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _connectLockActionViewModel.ConnectLockAsync();
return 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()
{
Log.ForContext<BookedDisconnected>().Error("Click of unsupported button click detected.");
return await Task.FromResult<IRequestHandler>(this);
}
}
}

View file

@ -0,0 +1,264 @@
using System;
using System.Threading.Tasks;
using Serilog;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using CloseCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
using ShareeBike.Services.BluetoothLock.Exception;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class BookedOpen : Base, IRequestHandler, CloseCommand.ICloseCommandListener
{
/// <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,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionCloseLock, // Close Lock
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = activeUser.DebugLevel.HasFlag(Model.User.Account.Permissions.ManageAlarmAndSounds) ? AppResources.ActionLockSettings : GetType().Name;
IsLockitButtonVisible = activeUser.DebugLevel.HasFlag(Model.User.Account.Permissions.ManageAlarmAndSounds);
_closeLockActionViewModel = new CloseLockActionViewModel<BookedOpen>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for close action.
/// </summary>
private readonly CloseLockActionViewModel<BookedOpen> _closeLockActionViewModel;
/// <summary>
/// Processes the close lock progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(CloseCommand.Step step) => _closeLockActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the close lock state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(CloseCommand.State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
/// <summary> Close lock (and return bike).</summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _closeLockActionViewModel.CloseLockAsync();
if(_closeLockActionViewModel.IsEndRentalRequested == false)
{
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
var _endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
ViewUpdateManager,
ViewService,
BikesViewModel);
await _endRentalActionViewModel.EndRentalAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
public async Task<IRequestHandler> HandleRequestOption2()
{
if (ActiveUser.DebugLevel.HasFlag(Model.User.Account.Permissions.ManageAlarmAndSounds))
{
await ManageLockSettings();
}
else
{
throw new InvalidOperationException();
}
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary> Manage sound/ alarm settings. </summary>
/// <returns></returns>
public async Task<IRequestHandler> ManageLockSettings()
{
// Stop polling before requesting bike.
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopAsync();
// Go to lock settings
Log.ForContext<ReservedOpen>().Information("User selected bike {bikeId} in order to manage sound/ alarm settings.", SelectedBike.Id);
// Alarm settings
var resultAlarm = await ViewService.DisplayAlert(
"Alarm einstellen",
"Alarm auf Einstellung 'niedrige Sensitivität' setzen oder Alarm ausschalten?",
"Auf niedrige Sensitivität",
"Alarm aus");
try
{
if (resultAlarm == true)
{
// Lower alarm sensitivity.
BikesViewModel.ActionText = "Setzen Alarm-Einstellungen...";
await LockService[SelectedBike.LockInfo.Id].SetAlarmSettingsAsync(AlarmSettings.SmallSensivitySilent);
}
else
{
// Switch off alarm.
BikesViewModel.ActionText = "Alarm ausschalten...";
await LockService[SelectedBike.LockInfo.Id].SetIsAlarmOffAsync(true);
}
}
catch (OutOfReachException exception)
{
Log.ForContext<ReservedOpen>().Debug("Can not set alarm settings. {Exception}", exception);
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
"Fehler beim Setzen der Alarm-Einstellungen!",
"Alarm kann erst eingestellt werden, wenn Rad in der Nähe ist.",
AppResources.MessageAnswerOk);
return this;
}
catch (Exception exception)
{
Log.ForContext<ReservedOpen>().Error("Can not set alarm settings. {Exception}", exception);
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
"Fehler beim Setzen der Alarms-Einstellungen!",
exception.Message,
AppResources.MessageAnswerOk);
return this;
}
// Sound settings
var resultSound = await ViewService.DisplayAlert(
"Sounds einstellen",
"Sounds auf Einstellung 'Warnung' setzen oder alle Sounds ausschalten?",
"Auf Warnung",
"Alles aus");
try
{
if (resultSound == true)
{
BikesViewModel.ActionText = "Sounds einstellen auf 'Warnung'...";
await LockService[SelectedBike.LockInfo.Id].SetSoundAsync(SoundSettings.Warn);
}
else
{
BikesViewModel.ActionText = "Alle Sounds ausschalten...";
await LockService[SelectedBike.LockInfo.Id].SetSoundAsync(SoundSettings.AllOff);
}
}
catch (OutOfReachException exception)
{
Log.ForContext<ReservedOpen>().Debug("Can not set sounds. {Exception}", exception);
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
"Fehler beim Einstellen der Sounds!",
"Sounds können erst eingestellt werden, wenn Rad in der Nähe ist.",
AppResources.MessageAnswerOk);
return this;
}
catch (Exception exception)
{
Log.ForContext<ReservedOpen>().Error("Can not set sounds. {Exception}", exception);
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
"Fehler beim Einstellen der Sounds!",
exception.Message,
AppResources.MessageAnswerOk);
return Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, GeolocationService, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartAsync(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return this;
}
/// <summary> Request is not supported, button should be disabled. </summary>
/// <returns></returns>
public async Task<IRequestHandler> UnsupportedRequest()
{
Log.ForContext<DisposableDisconnected>().Error("Click of unsupported button click detected.");
return await Task.FromResult<IRequestHandler>(this);
}
}
}

View file

@ -0,0 +1,163 @@
using System;
using System.Threading.Tasks;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using CloseCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
using OpenCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.OpenCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class BookedUnknown : Base, IRequestHandler, CloseCommand.ICloseCommandListener, OpenCommand.IOpenCommandListener
{
/// <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 BookedUnknown(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionOpenLock, // Open Lock
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
IsLockitButtonVisible = true; // Show button
_openLockActionViewModel = new OpenLockActionViewModel<BookedUnknown>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
_closeLockActionViewModel = new CloseLockActionViewModel<BookedUnknown>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for close action.
/// </summary>
private readonly OpenLockActionViewModel<BookedUnknown> _openLockActionViewModel;
/// <summary>
/// Processes the open lock progress.
/// </summary>
/// <param name="step">Current step to process.</param>
public void ReportStep(OpenCommand.Step step) => _openLockActionViewModel.ReportStep(step);
/// <summary>
/// Processes the open lock state.
/// </summary>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(OpenCommand.State state, string details) => await _openLockActionViewModel.ReportStateAsync(state, details);
/// <summary> Open bike and update COPRI lock state. </summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _openLockActionViewModel.OpenLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary>
/// Holds the view model for close action.
/// </summary>
private readonly CloseLockActionViewModel<BookedUnknown> _closeLockActionViewModel;
/// <summary>
/// Processes the close lock progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(CloseCommand.Step step) => _closeLockActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the close lock state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(CloseCommand.State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
public async Task<IRequestHandler> HandleRequestOption2()
{
await _closeLockActionViewModel.CloseLockAsync();
if (_closeLockActionViewModel.IsEndRentalRequested == false)
{
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
var _endRentalActionViewModel = new EndRentalActionViewModel<BookedClosed>(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
ViewUpdateManager,
ViewService,
BikesViewModel);
await _endRentalActionViewModel.EndRentalAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
}
}

View file

@ -0,0 +1,130 @@
using System;
using System.Threading.Tasks;
using Serilog;
using ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using StartReservationCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartReservationCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class DisposableDisconnected : Base, IRequestHandler, StartReservationCommand.IStartReservationCommandListener
{
/// <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 DisposableDisconnected(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionRequestBike, // Reserve / Rent Bike
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = GetType().Name;
IsLockitButtonVisible = false;
_startReservationOrRentalActionViewModel = new StartReservationOrRentalActionViewModel<DisposableDisconnected>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly StartReservationOrRentalActionViewModel<DisposableDisconnected> _startReservationOrRentalActionViewModel;
/// <summary>
/// Processes the reserving a bike progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(StartReservationCommand.Step step) => _startReservationOrRentalActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the reserving a bike state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(StartReservationCommand.State state, string details) => await _startReservationOrRentalActionViewModel.ReportStateAsync(state, details);
/// <summary>Reserve bike, connect to lock, open lock and rent bike.</summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _startReservationOrRentalActionViewModel.StartReservationOrRentalAsync();
if (_startReservationOrRentalActionViewModel.ContinueWithOpenLock == false)
{
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
var _openLockActionViewModel = new OpenLockActionViewModel<BookedClosed>(
SelectedBike,
ViewUpdateManager,
ViewService,
BikesViewModel);
await _openLockActionViewModel.OpenLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
/// <summary> Request is not supported, button should be disabled. </summary>
/// <returns></returns>
public async Task<IRequestHandler> UnsupportedRequest()
{
Log.ForContext<DisposableDisconnected>().Error("Click of unsupported button click detected.");
return await Task.FromResult<IRequestHandler>(this);
}
}
}

View file

@ -0,0 +1,164 @@
using System;
using System.Threading.Tasks;
using ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using StartReservationCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartReservationCommand;
using CloseCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
/// <summary> Bike is disposable, lock is open and connected to app. </summary>
/// <remarks>
/// This state can not be occur because
/// - app does not allow to return bike/ cancel reservation when lock is not closed
/// - as long as app is connected to lock
/// - lock can not be opened manually
/// - no other device can access lock
/// </remarks>
public class DisposableOpen : Base, IRequestHandler, StartReservationCommand.IStartReservationCommandListener, CloseCommand.ICloseCommandListener
{
/// <summary> Bike is disposable, lock is open and can be reached via bluetooth. </summary>
/// <remarks>
/// This state should never occur because as long as a ILOCKIT is connected it
/// - cannot be closed manually
/// - no other device can access lock
/// - app itself should never event attempt to open a lock which is not rented.
/// </remarks>
/// <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 DisposableOpen(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionRequestBike, // Reserve / Rent Bike
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = AppResources.ActionCloseLock; // Close Lock
IsLockitButtonVisible = true; // Show button
_startReservationOrRentalActionViewModel = new StartReservationOrRentalActionViewModel<DisposableOpen>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
_closeLockActionViewModel = new CloseLockActionViewModel<DisposableOpen>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly StartReservationOrRentalActionViewModel<DisposableOpen> _startReservationOrRentalActionViewModel;
/// <summary>
/// Processes the reserving a bike progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(StartReservationCommand.Step step) => _startReservationOrRentalActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the reserving a bike state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(StartReservationCommand.State state, string details) => await _startReservationOrRentalActionViewModel.ReportStateAsync(state, details);
/// <summary>
/// Holds the view model for close action.
/// </summary>
private readonly CloseLockActionViewModel<DisposableOpen> _closeLockActionViewModel;
/// <summary>
/// Processes the close lock progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(CloseCommand.Step step) => _closeLockActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the close lock state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(CloseCommand.State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
/// <summary>Rents bike by reserving bike and renting bike.</summary>
/// <returns>Next request handler.</returns>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _startReservationOrRentalActionViewModel.StartReservationOrRentalAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary>Closes Lock.</summary>
/// <returns>Next request handler.</returns>
public async Task<IRequestHandler> HandleRequestOption2()
{
await _closeLockActionViewModel.CloseLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
}
}

View file

@ -0,0 +1,26 @@
using System.Threading.Tasks;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock
{
public interface IRequestHandler : IRequestHandlerBase
{
/// <summary> Gets a value indicating whether the ILockIt button which is managed by request handler is visible or not. </summary>
bool IsLockitButtonVisible { get; }
/// <summary> Gets the text of the ILockIt button which is managed by request handler. </summary>
string LockitButtonText { get; }
/// <summary>
/// Performs the copri action to be executed when user presses the copri button managed by request handler.
/// </summary>
/// <returns>New handler object if action succeeded, same handler otherwise.</returns>
Task<IRequestHandler> HandleRequestOption1();
Task<IRequestHandler> HandleRequestOption2();
/// <summary>
/// Holds error description (invalid state).
/// </summary>
string ErrorText { get; }
}
}

View file

@ -0,0 +1,56 @@
using System;
using System.Threading.Tasks;
using Serilog;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.State;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class InvalidState : IRequestHandler
{
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
public InvalidState(
IBikesViewModel bikesViewModel,
InUseStateEnum copriState,
LockingState lockingState,
string errorText)
{
BikesViewModel = bikesViewModel
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null.");
ErrorText = errorText;
Log.Error($"{errorText}. Copri state is {copriState} and lock state is {lockingState}.");
}
/// <summary>View model to be used for progress report and unlocking/ locking view.</summary>
public IBikesViewModel BikesViewModel { get; }
public bool IsLockitButtonVisible => false;
public string LockitButtonText => GetType().Name;
public bool IsConnected => false;
public bool IsButtonVisible => false;
public string ButtonText => GetType().Name;
/// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false;
public string ErrorText { get; }
public async Task<IRequestHandler> HandleRequestOption2()
{
Log.ForContext<InvalidState>().Error($"Click of unsupported button {nameof(HandleRequestOption2)} detected.");
return await Task.FromResult<IRequestHandler>(this);
}
public async Task<IRequestHandler> HandleRequestOption1()
{
Log.ForContext<InvalidState>().Error($"Click of unsupported button {nameof(HandleRequestOption1)} detected.");
return await Task.FromResult<IRequestHandler>(this);
}
}
}

View file

@ -0,0 +1,88 @@
using System;
using System.Threading.Tasks;
using Serilog;
using ShareeBike.Model.State;
using ShareeBike.MultilingualResources;
using ShareeBike.View;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock
{
public class NotLoggedIn : IRequestHandler
{
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
public NotLoggedIn(
InUseStateEnum state,
IViewService viewService,
IBikesViewModel bikesViewModel)
{
ButtonText = BC.StateToText.GetActionText(state);
ViewService = viewService;
BikesViewModel = bikesViewModel
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null.");
}
/// <summary>View model to be used for progress report and unlocking/ locking view.</summary>
public IBikesViewModel BikesViewModel { get; }
public bool IsButtonVisible => true;
public bool IsLockitButtonVisible => false;
public string ButtonText { get; private set; }
public string LockitButtonText => GetType().Name;
/// <summary>
/// Reference on view service to show modal notifications and to perform navigation.
/// </summary>
private IViewService ViewService { get; }
public bool IsConnected => throw new NotImplementedException();
/// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false;
public async Task<IRequestHandler> HandleRequestOption1()
{
Log.ForContext<BikesViewModel>().Information("User selected bike but is not logged in.");
// User is not logged in
BikesViewModel.ActionText = string.Empty;
var l_oResult = await ViewService.DisplayAlert(
AppResources.QuestionLogInTitle,
AppResources.QuestionLogIn,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
if (l_oResult == false)
{
// User aborted booking process
BikesViewModel.IsIdle = true;
return this;
}
try
{
// Switch to map page
await ViewService.ShowPage("//LoginPage");
}
catch (Exception p_oException)
{
Log.ForContext<BikesViewModel>().Error("Ein unerwarteter Fehler ist in der Klasse NotLoggedIn aufgetreten. Kontext: Aufruf nach Reservierungsversuch ohne Anmeldung. {@Exception}", p_oException);
BikesViewModel.IsIdle = true;
return this;
}
BikesViewModel.IsIdle = true;
return this;
}
public async Task<IRequestHandler> HandleRequestOption2()
{
Log.ForContext<NotLoggedIn>().Error("Click of unsupported button detected.");
return await Task.FromResult(this);
}
public string ErrorText => string.Empty;
}
}

View file

@ -0,0 +1,173 @@
using System;
using System.Threading.Tasks;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using IBikeInfoMutable = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable;
using CancelReservationCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.CancelReservationCommand;
using StartRentalCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartRentalCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
/// <summary> Bike is reserved, lock is closed and connected to app. </summary>
/// <remarks>
/// Occurs when
/// - bike was reserved out of reach and is in reach now
/// - bike is reserved while in reach
/// </remarks>
public class ReservedClosed : Base, IRequestHandler, CancelReservationCommand.ICancelReservationCommandListener, StartRentalCommand.IStartRentalCommandListener
{
/// <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 ReservedClosed(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionCancelReservation, // Cancel Reservation
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = AppResources.ActionOpenLockAndRentBike; // Rent Bike
IsLockitButtonVisible = true; // Show button
_cancelReservationActionViewModel = new CancelReservationActionViewModel<ReservedClosed>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
_startRentalActionViewModel = new StartReservationOrRentalActionViewModel<ReservedClosed>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly CancelReservationActionViewModel<ReservedClosed> _cancelReservationActionViewModel;
/// <summary>
/// Processes the canceling of reservation progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(CancelReservationCommand.Step step) => _cancelReservationActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the canceling of reservation state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(CancelReservationCommand.State state, string details) => await _cancelReservationActionViewModel.ReportStateAsync(state, details);
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly StartReservationOrRentalActionViewModel<ReservedClosed> _startRentalActionViewModel;
/// <summary>
/// Processes the renting a bike progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(StartRentalCommand.Step step) => _startRentalActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the renting a bike state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(StartRentalCommand.State state, string details) => await _startRentalActionViewModel.ReportStateAsync(state, details);
/// <summary> Cancel reservation. </summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _cancelReservationActionViewModel.CancelReservationAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary> Open lock and rent bike. </summary>
public async Task<IRequestHandler> HandleRequestOption2()
{
await _startRentalActionViewModel.StartReservationOrRentalAsync();
if (_startRentalActionViewModel.ContinueWithOpenLock == false)
{
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
var _openLockActionViewModel = new OpenLockActionViewModel<BookedClosed>(
SelectedBike,
ViewUpdateManager,
ViewService,
BikesViewModel);
await _openLockActionViewModel.OpenLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
}
}

View file

@ -0,0 +1,167 @@
using System;
using System.Threading.Tasks;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using CancelReservationCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.CancelReservationCommand;
using StartRentalCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartRentalCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class ReservedDisconnected : Base, IRequestHandler, CancelReservationCommand.ICancelReservationCommandListener, StartRentalCommand.IStartRentalCommandListener
{
/// <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 ReservedDisconnected(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionCancelReservation, // Cancel Reservation
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = AppResources.ActionRentBike; // Rent bike
IsLockitButtonVisible = true; // Show button
_cancelReservationlActionViewModel = new CancelReservationActionViewModel<ReservedDisconnected>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
_startRentalActionViewModel = new StartReservationOrRentalActionViewModel<ReservedDisconnected>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly CancelReservationActionViewModel<ReservedDisconnected> _cancelReservationlActionViewModel;
/// <summary>
/// Processes the canceling of reservation progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(CancelReservationCommand.Step step) => _cancelReservationlActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the canceling of reservation state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(CancelReservationCommand.State state, string details) => await _cancelReservationlActionViewModel.ReportStateAsync(state, details);
/// <summary> Cancel reservation. </summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _cancelReservationlActionViewModel.CancelReservationAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly StartReservationOrRentalActionViewModel<ReservedDisconnected> _startRentalActionViewModel;
/// <summary>
/// Processes the renting a bike progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(StartRentalCommand.Step step) => _startRentalActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the renting a bike state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(StartRentalCommand.State state, string details) => await _startRentalActionViewModel.ReportStateAsync(state, details);
/// <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 _startRentalActionViewModel.StartReservationOrRentalAsync();
if (_startRentalActionViewModel.ContinueWithOpenLock == false)
{
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
var _openLockActionViewModel = new OpenLockActionViewModel<BookedClosed>(
SelectedBike,
ViewUpdateManager,
ViewService,
BikesViewModel);
await _openLockActionViewModel.OpenLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
}
}

View file

@ -0,0 +1,152 @@
using System;
using System.Threading.Tasks;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using IBikeInfoMutable = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable;
using CloseCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
using StartRentalCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartRentalCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
/// <summary> Bike is reserved, lock is open and connected to app. </summary>
/// <remarks>
/// This state might occur when a ILOCKIT was manually opened (color code) and app connects afterwards.
/// This should never during ILOCKIT is connected to app because
/// - manually opening lock is not possible when lock is connected
/// - two devices can not simultaneously connect to same lock.
public class ReservedOpen : Base, IRequestHandler, CloseCommand.ICloseCommandListener, StartRentalCommand.IStartRentalCommandListener
{
/// <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 ReservedOpen(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionCloseLock, // Close Lock
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = AppResources.ActionRentBike; // Rent bike
IsLockitButtonVisible = true; // Show button
_closeLockActionViewModel = new CloseLockActionViewModel<ReservedOpen>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
_startRentalActionViewModel = new StartReservationOrRentalActionViewModel<ReservedOpen>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for close action.
/// </summary>
private readonly CloseLockActionViewModel<ReservedOpen> _closeLockActionViewModel;
/// <summary>
/// Processes the close lock progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(CloseCommand.Step step) => _closeLockActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the close lock state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(CloseCommand.State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly StartReservationOrRentalActionViewModel<ReservedOpen> _startRentalActionViewModel;
/// <summary>
/// Processes the renting a bike progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(StartRentalCommand.Step step) => _startRentalActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the renting a bike state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(StartRentalCommand.State state, string details) => await _startRentalActionViewModel.ReportStateAsync(state, details);
/// <summary> Close lock. </summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _closeLockActionViewModel.CloseLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary> Start Rental. </summary>
/// <returns></returns>
public async Task<IRequestHandler> HandleRequestOption2()
{
await _startRentalActionViewModel.StartReservationOrRentalAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
}
}

View file

@ -0,0 +1,165 @@
using System;
using System.Threading.Tasks;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.User;
using ShareeBike.MultilingualResources;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.Geolocation;
using ShareeBike.View;
using CloseCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
using StartRentalCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartRentalCommand;
using static ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandlerFactory;
namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
public class ReservedUnknown : Base, IRequestHandler, CloseCommand.ICloseCommandListener, StartRentalCommand.IStartRentalCommandListener
{
/// <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 ReservedUnknown(
IBikeInfoMutable selectedBike,
Func<bool> isConnectedDelegate,
Func<bool, IConnector> connectorFactory,
IGeolocationService geolocation,
ILocksService lockService,
Func<IPollingUpdateTaskManager> viewUpdateManager,
ISmartDevice smartDevice,
IViewService viewService,
IBikesViewModel bikesViewModel,
IUser activeUser) : base(
selectedBike,
AppResources.ActionCloseLock, // Close Lock
true, // Show button
isConnectedDelegate,
connectorFactory,
geolocation,
lockService,
viewUpdateManager,
smartDevice,
viewService,
bikesViewModel,
activeUser)
{
LockitButtonText = AppResources.ActionRentBike; // Rent Bike
IsLockitButtonVisible = true; // Show button
_closeLockActionViewModel = new CloseLockActionViewModel<ReservedUnknown>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
_startRentalActionViewModel = new StartReservationOrRentalActionViewModel<ReservedUnknown>(
selectedBike,
viewUpdateManager,
viewService,
bikesViewModel);
}
/// <summary>
/// Holds the view model for close action.
/// </summary>
private readonly CloseLockActionViewModel<ReservedUnknown> _closeLockActionViewModel;
/// <summary>
/// Processes the close lock progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(CloseCommand.Step step) => _closeLockActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the close lock state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(CloseCommand.State state, string details) => await _closeLockActionViewModel.ReportStateAsync(state, details);
/// <summary>
/// Holds the view model for requesting a bike action.
/// </summary>
private readonly StartReservationOrRentalActionViewModel<ReservedUnknown> _startRentalActionViewModel;
/// <summary>
/// Processes the renting a bike progress.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="step">Current step to process.</param>
public void ReportStep(StartRentalCommand.Step step) => _startRentalActionViewModel?.ReportStep(step);
/// <summary>
/// Processes the renting a bike state.
/// </summary>
/// <remarks>
/// Only used for testing.
/// </remarks>
/// <param name="state">State to process.</param>
/// <param name="details">Textual details describing current state.</param>
public async Task ReportStateAsync(StartRentalCommand.State state, string details) => await _startRentalActionViewModel.ReportStateAsync(state, details);
/// <summary> Open bike and update COPRI lock state. </summary>
public async Task<IRequestHandler> HandleRequestOption2()
{
await _startRentalActionViewModel.StartReservationOrRentalAsync();
if (_startRentalActionViewModel.ContinueWithOpenLock == false)
{
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
var _openLockActionViewModel = new OpenLockActionViewModel<BookedClosed>(
SelectedBike,
ViewUpdateManager,
ViewService,
BikesViewModel);
await _openLockActionViewModel.OpenLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
/// <summary> Close lock (and return bike).</summary>
public async Task<IRequestHandler> HandleRequestOption1()
{
await _closeLockActionViewModel.CloseLockAsync();
return Create(
SelectedBike,
IsConnectedDelegate,
ConnectorFactory,
GeolocationService,
LockService,
ViewUpdateManager,
SmartDevice,
ViewService,
BikesViewModel,
ActiveUser);
}
}
}