using System; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ShareeBike.Model.Connector; using ShareeBike.Model.Device; using ShareeBike.Model.User; using ShareeBike.View; using BikeInfoMutable = ShareeBike.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable; namespace ShareeBike.ViewModel.Bikes.Bike.CopriLock { using IRequestHandler = BluetoothLock.IRequestHandler; /// /// View model for a ILockIt bike. /// Provides functionality for views /// - MyBikes /// - BikesAtStation /// public class BikeViewModel : BikeViewModelBase, INotifyPropertyChanged { /// Notifies GUI about changes. public override event PropertyChangedEventHandler PropertyChanged; /// Holds object which manages requests. private IRequestHandler RequestHandler { get; set; } /// Raises events in order to update GUI. public override void RaisePropertyChanged(object sender, PropertyChangedEventArgs eventArgs) => PropertyChanged?.Invoke(sender, eventArgs); /// Raises events if property values changed in order to update GUI. private void RaisePropertyChangedEvent( IRequestHandler lastHandler, string lastStateText = null, Xamarin.Forms.Color? lastStateColor = null) { if (lastHandler.ButtonText != ButtonText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonText))); } if (lastHandler.IsButtonVisible != IsButtonVisible) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsButtonVisible))); } if (lastHandler.LockitButtonText != LockitButtonText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LockitButtonText))); } if (lastHandler.IsLockitButtonVisible != IsLockitButtonVisible) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLockitButtonVisible))); } if (RequestHandler.ErrorText != lastHandler.ErrorText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ErrorText))); } if (!string.IsNullOrEmpty(lastStateText) && lastStateText != StateText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StateText))); } if (lastStateColor != null && lastStateColor != StateColor) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StateColor))); } } /// /// Constructs a bike view model object. /// /// Provides info about the smart device (phone, tablet, ...) /// Bike to be displayed. /// Object holding logged in user or an empty user object. /// Holds the view context in which bike view model is used. /// Provides in use state information. /// View model to be used for progress report and unlocking/ locking view. /// Delegate to open browser. public BikeViewModel( Func isConnectedDelegate, Func connectorFactory, Action bikeRemoveDelegate, Func viewUpdateManager, ISmartDevice smartDevice, IViewService viewService, BikeInfoMutable selectedBike, IUser user, ViewContext viewContext, IInUseStateInfoProvider stateInfoProvider, IBikesViewModel bikesViewModel, Action openUrlInBrowser) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, user, viewContext, stateInfoProvider, bikesViewModel, openUrlInBrowser) { RequestHandler = user.IsLoggedIn ? RequestHandlerFactory.Create( selectedBike, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, user) : new BluetoothLock.NotLoggedIn( selectedBike.State.Value, viewService, bikesViewModel); selectedBike.PropertyChanged += (sender, ev) => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsButtonVisible))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLockitButtonVisible))); }; } /// /// Handles BikeInfoMutable events. /// Helper member to raise events. Maps model event change notification to view model events. /// Todo: Check which events are received here and filter, to avoid event storm. /// public override void OnSelectedBikeStateChanged() { var lastHandler = RequestHandler; RequestHandler = RequestHandlerFactory.Create( Bike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); RaisePropertyChangedEvent(lastHandler); } /// Gets visibility of the copri command button. public bool IsButtonVisible => RequestHandler.IsButtonVisible && Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ; /// Gets the text of the copri command button. public string ButtonText => RequestHandler.ButtonText; /// Gets visibility of the ILockIt command button. public bool IsLockitButtonVisible => RequestHandler.IsLockitButtonVisible && Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ; /// Gets the text of the ILockIt command button. public string LockitButtonText => RequestHandler.LockitButtonText; /// Processes request to perform a copri action (reserve bike and cancel reservation). public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption1())); /// Processes request to perform a ILockIt action (unlock bike and lock bike). public System.Windows.Input.ICommand OnLockitButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption2())); /// Processes request to perform a copri action (reserve bike and cancel reservation). private async Task ClickButton(Task handleRequest) { var lastHandler = RequestHandler; var lastState = Bike.State.Value; var lastStateText = StateText; var lastStateColor = StateColor; RequestHandler = await handleRequest; CheckRemoveBike(Id, lastState); if (RuntimeHelpers.Equals(lastHandler, RequestHandler)) { // No state change occurred (same instance is returned). return; } RaisePropertyChangedEvent( lastHandler, lastStateText, lastStateColor); } public string ErrorText => RequestHandler.ErrorText; } }