Version 3.0.366

This commit is contained in:
Anja 2023-06-06 12:00:24 +02:00
parent 0eb7362cb8
commit 24cdfbb0ca
84 changed files with 900 additions and 393 deletions

View file

@ -95,7 +95,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC
}
}
/// <summary> Gets visiblity of the copri command button. </summary>
/// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;

View file

@ -1,4 +1,4 @@
using System;
using System;
using TINK.Model.Connector;
using TINK.Model.Device;
using TINK.Model.User;
@ -62,12 +62,12 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
}
}
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary>
/// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired { get; set; }
/// <summary>
/// Constructs the reqest handler base.
/// 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>

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BC;
@ -55,7 +55,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
/// <summary>Gets the is connected state. </summary>
public bool IsConnected { get; set; }
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary>
/// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false;
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>

View file

@ -37,7 +37,10 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
var l_oResult = await ViewService.DisplayAlert(
string.Empty,
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes),
string.Format(
AppResources.QuestionReserveBike,
SelectedBike.GetFullDisplayName(),
SelectedBike.TariffDescription?.MaxReservationTimeSpan.TotalMinutes ?? 0),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);

View file

@ -177,14 +177,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
RaisePropertyChangedEvent(lastHandler);
}
/// <summary> Gets visiblity of the copri command button. </summary>
/// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible;
/// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText;
/// <summary> Gets visiblity of the ILockIt command button. </summary>
/// <summary> Gets visibility of the ILockIt command button. </summary>
public bool IsLockitButtonVisible
=> RequestHandler.IsLockitButtonVisible;

View file

@ -11,7 +11,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable>
{
/// <summary>
/// Constructs the reqest handler base.
/// 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>

View file

@ -62,7 +62,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Ask whether to really reserve bike?
var alertResult = await ViewService.DisplayAlert(
string.Empty,
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes),
string.Format(
AppResources.QuestionReserveBike,
SelectedBike.GetFullDisplayName(),
SelectedBike.TariffDescription?.MaxReservationTimeSpan.TotalMinutes ?? 0),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);

View file

@ -1,10 +1,10 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
{
public interface IRequestHandler : IRequestHandlerBase
{
/// <summary> Gets a value indicating whether the ILockIt button which is managed by request hadnler is visible or not. </summary>
/// <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>
@ -13,13 +13,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
/// <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 suceeded, same handler otherwise.</returns>
/// <returns>New handler object if action succeeded, same handler otherwise.</returns>
Task<IRequestHandler> HandleRequestOption1();
Task<IRequestHandler> HandleRequestOption2();
/// <summary>
/// Holds error discription (invalid state).
/// Holds error description (invalid state).
/// </summary>
string ErrorText { get; }
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
@ -36,7 +36,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
public string ButtonText => GetType().Name;
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary>
/// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false;
public string ErrorText { get; }

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Serilog;
using TINK.Model.State;
@ -38,7 +38,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
public bool IsConnected => throw new NotImplementedException();
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary>
/// <summary> Gets if the bike has to be removed after action has been completed. </summary>
public bool IsRemoveBikeRequired => false;
public async Task<IRequestHandler> HandleRequestOption1()

View file

@ -136,7 +136,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
RaisePropertyChangedEvent(lastHandler);
}
/// <summary> Gets visiblity of the copri command button. </summary>
/// <summary> Gets visibility of the copri command button. </summary>
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;
@ -144,7 +144,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
/// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText;
/// <summary> Gets visiblity of the ILockIt command button. </summary>
/// <summary> Gets visibility of the ILockIt command button. </summary>
public bool IsLockitButtonVisible
=> RequestHandler.IsLockitButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;

View file

@ -1,4 +1,4 @@
using System;
using System;
using TINK.Model.Connector;
using TINK.Model.Device;
using TINK.Model.User;
@ -9,7 +9,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable>
{
/// <summary>
/// Constructs the reqest handler base.
/// 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>

View file

@ -132,7 +132,10 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Ask whether to really reserve bike?
var alertResult = await ViewService.DisplayAlert(
string.Empty,
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes),
string.Format(
AppResources.QuestionReserveBike,
SelectedBike.GetFullDisplayName(),
SelectedBike.TariffDescription?.MaxReservationTimeSpan.TotalMinutes ?? 0),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);

View file

@ -1,4 +1,4 @@
using System;
using System;
using Serilog;
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
using TINK.Model.Connector;
@ -54,7 +54,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
case Model.State.InUseStateEnum.Disposable:
// Bike is reserved, selecte action depending on lock state.
// Bike is reserved, selected action depending on lock state.
return new DisposableClosed(
selectedCopriLock,
isConnectedDelegate,
@ -79,7 +79,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
case Model.State.InUseStateEnum.Booked:
// Bike is booked, selecte action depending on lock state.
// Bike is booked, selected action depending on lock state.
var lockState = selectedCopriLock.LockInfo.State;
switch (lockState)
{

View file

@ -1,4 +1,4 @@
namespace TINK.ViewModel.Bikes.Bike
namespace TINK.ViewModel.Bikes.Bike
{
/// <summary>
/// Base interface for Copri and ILockIt request handler.
@ -22,7 +22,7 @@
/// <summary>Gets the is connected state. </summary>
bool IsConnected { get; }
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary>
/// <summary> Gets if the bike has to be removed after action has been completed. </summary>
bool IsRemoveBikeRequired { get; }
}
}

View file

@ -13,7 +13,7 @@ namespace TINK.ViewModel.Bikes.Bike
private const string RIDETYPEKEY = "AAFAHRTEN";
public TariffDescriptionViewModel(RentalDescription tariff)
public TariffDescriptionViewModel(IRentalDescription tariff)
{
Name = tariff?.Name ?? string.Empty;

View file

@ -1,5 +1,4 @@
using System;
using TINK.Model.State;
using System;
using TINK.MultilingualResources;
using TINK.ViewModel.Bikes.Bike;
@ -8,7 +7,7 @@ namespace TINK.ViewModel
public class BikeAtStationInUseStateInfoProvider : IInUseStateInfoProvider
{
/// <summary> Gets reserved into display text. </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetReservedInfo(
TimeSpan? remainingTime,
@ -21,10 +20,10 @@ namespace TINK.ViewModel
if (string.IsNullOrEmpty(code))
{
// Reservation code not available
return string.Format(AppResources.StatusTextReservationExpiredMaximumReservationTime, StateRequestedInfo.MaximumReserveTime.Minutes);
return AppResources.StatusTextReservationExpiredMaximumReservationTime;
}
return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code, StateRequestedInfo.MaximumReserveTime.Minutes);
return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code);
}
if (!string.IsNullOrEmpty(code))
@ -36,7 +35,7 @@ namespace TINK.ViewModel
}
/// <summary> Gets booked into display text. </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetBookedInfo(
DateTime? from,

View file

@ -10,10 +10,13 @@ using Plugin.BLE.Abstractions.Contracts;
using Serilog;
using TINK.Model;
using TINK.Model.Bikes;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
using TINK.Model.Connector;
using TINK.Model.Connector.Filter;
using TINK.Model.Device;
using TINK.Model.Services.CopriApi;
using TINK.Model.State;
using TINK.Model.Stations.StationNS;
using TINK.Model.User;
using TINK.MultilingualResources;
@ -25,6 +28,7 @@ using TINK.Services.Permissions;
using TINK.Settings;
using TINK.View;
using TINK.ViewModel.Bikes;
using TINK.ViewModel.Map;
using Xamarin.Essentials;
using Xamarin.Forms;
using Command = Xamarin.Forms.Command;
@ -84,13 +88,13 @@ namespace TINK.ViewModel.FindBike
/// <summary>
/// True if ListView of Bikes is refreshing after user pulled;
/// </summary>
private bool _isRefreshing = false;
private bool isRefreshing = false;
public bool IsRefreshing
{
get { return _isRefreshing; }
get { return isRefreshing; }
set
{
_isRefreshing = value;
isRefreshing = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing)));
}
}
@ -99,6 +103,7 @@ namespace TINK.ViewModel.FindBike
/// Holds what should be executed on pull to refresh
/// </summary>
public Command RefreshCommand { get; }
public Command ShowFilterBikeTypeInfoCommand { get; private set; }
/// <summary>
/// Constructs bike collection view model in case information about occupied bikes is available.
@ -146,6 +151,15 @@ namespace TINK.ViewModel.FindBike
await SelectBike();
});
ShowFilterBikeTypeInfoCommand = new Xamarin.Forms.Command(async () => {
await ViewService.DisplayAlert(
AppResources.MessageBikeTypeInfoTitle,
AppResources.MessageBikeTypeInfoText,
AppResources.MessageAnswerOk);
});
}
/// <summary>
@ -158,6 +172,8 @@ namespace TINK.ViewModel.FindBike
Log.ForContext<FindBikePageViewModel>().Information("User request to show page FindBike- page re-appearing");
ActiveFilteredBikeType = string.Empty;
IsConnected = IsConnectedDelegate();
// Stop polling before getting bikes info.
@ -185,6 +201,19 @@ namespace TINK.ViewModel.FindBike
ActionText = string.Empty;
IsIdle = true;
var result = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
var bikes = result.Response;
var exception = result.Exception;
if (exception != null)
{
Log.ForContext<MapPageViewModel>().Error("Getting bikes in polling context failed with exception {Exception}.", exception);
}
// Get Active Filtered BikeType
GetActiveFilteredBikeType(bikes);
}
/// <summary> Command object to bind select bike button to view model. </summary>
@ -209,7 +238,7 @@ namespace TINK.ViewModel.FindBike
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed failed (Copri server not reachable).");
Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed (Copri server not reachable).");
await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle,
@ -402,6 +431,9 @@ namespace TINK.ViewModel.FindBike
IsIdle = true;
return;
}
BikeIdUserInput = string.Empty;
}
/// <summary> Create task which updates my bike view model.</summary>
@ -417,13 +449,12 @@ namespace TINK.ViewModel.FindBike
null);
var result = ConnectorFactory(IsConnected).Query.GetBikesAsync().Result;
var bikes = result.Response;
var exception = result.Exception;
if (exception != null)
{
Log.ForContext<FindBikePageViewModel>().Error("Getting bikes occupied in polling context failed with exception {Exception}.", exception);
Log.ForContext<FindBikePageViewModel>().Error("Getting bikes in polling context failed with exception {Exception}.", exception);
}
var selectedBike = bikes.FirstOrDefault(x => x.Id.Equals(BikeIdUserInput.Trim(), StringComparison.OrdinalIgnoreCase));
@ -441,5 +472,44 @@ namespace TINK.ViewModel.FindBike
},
null);
}
private string activeFilteredBikeType = string.Empty;
/// <summary>
/// Selected Bike Type in MapFilter
/// </summary>
public string ActiveFilteredBikeType
{
get { return activeFilteredBikeType; }
set
{
if (value == activeFilteredBikeType)
{
return;
}
activeFilteredBikeType = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(ActiveFilteredBikeType)));
}
}
/// <summary>
/// Get Selected Bike Type in MapFilter
/// </summary>
public void GetActiveFilteredBikeType(BikeCollection bikesAll)
{
Log.ForContext<FindBikePageViewModel>().Debug($"Bike type of active filter is extracted.");
if (bikesAll != null)
{
var firstOrDefaultBikeType = bikesAll.FirstOrDefault().TypeOfBike;
if(firstOrDefaultBikeType == TypeOfBike.Cargo)
{
ActiveFilteredBikeType = AppResources.MarkingCargoBike;
}
else if(firstOrDefaultBikeType == TypeOfBike.City)
{
ActiveFilteredBikeType = AppResources.MarkingCityBike;
}
}
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
namespace TINK.ViewModel
{

View file

@ -50,7 +50,6 @@ namespace TINK.ViewModel.Map
/// </summary>
private Exception m_oException;
/// <summary>
/// Service to query/ manage permissions (location) of the app.
/// </summary>
@ -483,10 +482,23 @@ namespace TINK.ViewModel.Map
Log.ForContext<MapPageViewModel>().Verbose("Location permissions: {0}.", status);
}
private bool isLocationPermissionGranted = false;
/// <summary>
/// Exposes IsLocationPermissionGranted.
/// </summary>
public bool IsLocationPermissionGranted{ get; set;}
public bool IsLocationPermissionGranted
{
get => isLocationPermissionGranted;
set
{
if (value == isLocationPermissionGranted)
return;
Log.ForContext<MapPageViewModel>().Debug($"Switch value of {nameof(isLocationPermissionGranted)} to {value}.");
isLocationPermissionGranted = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLocationPermissionGranted)));
}
}
/// <summary>
/// Invoked when the auth cookie is not defined.
@ -655,6 +667,9 @@ namespace TINK.ViewModel.Map
Log.ForContext<MapPageViewModel>().Error("Getting bikes and stations in polling context failed with exception {Exception}.", exception);
}
// Get and expose status of location permission
GetLocationPermissionStatus();
// Load MyBikes Count -> MyBikes Icon/Button
GetMyBikesCount(resultStationsAndBikes.Response.BikesOccupied);

View file

@ -8,7 +8,7 @@ namespace TINK.ViewModel
public class MyBikeInUseStateInfoProvider : IInUseStateInfoProvider
{
/// <summary> Gets reserved into display text. </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetReservedInfo(
TimeSpan? remainingTime,
@ -17,25 +17,25 @@ namespace TINK.ViewModel
{
if (remainingTime == null)
{
// Reamining time is not available.
// Remaining time is not available.
if (stationId == null)
{
if (string.IsNullOrEmpty(code))
{
// Code is not available
return string.Format(AppResources.StatusTextReservationExpiredMaximumReservationTime, StateRequestedInfo.MaximumReserveTime.Minutes);
return AppResources.StatusTextReservationExpiredMaximumReservationTime;
}
return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code, StateRequestedInfo.MaximumReserveTime.Minutes);
return string.Format(AppResources.StatusTextReservationExpiredCodeMaxReservationTime, code);
}
if (string.IsNullOrEmpty(code))
{
return string.Format(AppResources.StatusTextReservationExpiredLocationMaxReservationTime, stationId, StateRequestedInfo.MaximumReserveTime.Minutes);
return string.Format(AppResources.StatusTextReservationExpiredLocationMaxReservationTime, stationId);
}
return string.Format(AppResources.StatusTextReservationExpiredCodeLocationMaxReservationTime, code, stationId, StateRequestedInfo.MaximumReserveTime.Minutes);
return string.Format(AppResources.StatusTextReservationExpiredCodeLocationMaxReservationTime, code, stationId);
}
if (!string.IsNullOrEmpty(stationId))
@ -63,7 +63,7 @@ namespace TINK.ViewModel
/// <summary>
/// Gets booked into display text.
/// </summary>
/// <todo>Log unexpeced states.</todo>
/// <todo>Log unexpected states.</todo>
/// <returns>Display text</returns>
public string GetBookedInfo(
DateTime? from,

View file

@ -12,8 +12,8 @@ namespace TINK.ViewModel
/// </summary>
public class PollingUpdateTask
{
/// <summary> Object to control canelling. </summary>
private CancellationTokenSource CanellationTokenSource { get; }
/// <summary> Object to control canceling. </summary>
private CancellationTokenSource CancellationTokenSource { get; }
/// <summary> Task to perform update. </summary>
private Task UpdateTask { get; }
@ -40,15 +40,15 @@ namespace TINK.ViewModel
var updatePeriodeSet = polling.Value;
CanellationTokenSource = new CancellationTokenSource();
CancellationTokenSource = new CancellationTokenSource();
int cycleIndex = 2;
UpdateTask = Task.Run(
async () =>
{
while (!CanellationTokenSource.IsCancellationRequested)
while (!CancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(updatePeriodeSet, CanellationTokenSource.Token);
await Task.Delay(updatePeriodeSet, CancellationTokenSource.Token);
{
// N. update cycle
Log.ForContext<PollingUpdateTask>().Information($"Actuating {cycleIndex} update cycle, context {GetType().Name} at {DateTime.Now}.");
@ -60,7 +60,7 @@ namespace TINK.ViewModel
}
}
},
CanellationTokenSource.Token);
CancellationTokenSource.Token);
}
/// <summary>
@ -76,13 +76,13 @@ namespace TINK.ViewModel
}
// Cancel update task;
if (CanellationTokenSource == null)
if (CancellationTokenSource == null)
{
throw new Exception($"Can not terminate periodical update task, context {GetType().Name} at {DateTime.Now}. No task running.");
}
Log.ForContext<PollingUpdateTask>().Information($"Request to terminate update cycle, context {GetType().Name} at {DateTime.Now}.");
CanellationTokenSource.Cancel();
CancellationTokenSource.Cancel();
try
{