Version 3.0.373

This commit is contained in:
Anja 2023-09-22 11:38:42 +02:00
parent f1cbab1d0a
commit 06428d96d9
87 changed files with 1796 additions and 1208 deletions

View file

@ -13,10 +13,8 @@ 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;
@ -29,7 +27,6 @@ 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;
@ -77,7 +74,7 @@ namespace TINK.ViewModel.FindBike
public BikeCollection Bikes { get; set; }
/// <summary> Do not allow to select bike if id is not set.</summary>
public bool IsSelectBikeEnabled => IsIdle && BikeIdUserInput != null && BikeIdUserInput.Length > 0;
public bool IsSelectBikeEnabled => IsIdle && BikeIdUserInput != null && BikeIdUserInput.Length > 1 && BikeIdUserInput.Any(x => char.IsLetter(x)) && BikeIdUserInput.Any(x => char.IsDigit(x));
/// <summary> Hide id input fields as soon as bike is found.</summary>
public bool IsSelectBikeVisible => BikeCollection != null && BikeCollection.Count == 0;
@ -85,6 +82,9 @@ namespace TINK.ViewModel.FindBike
/// <summary> Holds the stations to get station names form station ids. </summary>
private IEnumerable<IStation> Stations { get; }
/// <summary> Reference on the tink app instance. </summary>
private ITinkApp TinkApp { get; }
/// <summary>
/// True if ListView of Bikes is refreshing after user pulled;
/// </summary>
@ -124,6 +124,7 @@ namespace TINK.ViewModel.FindBike
/// <param name="openUrlInBrowser">Delegate to open browser.</param>
public FindBikePageViewModel(
User user,
ITinkApp tinkApp,
ILocationPermission permissions,
IBluetoothLE bluetoothLE,
string runtimPlatform,
@ -145,6 +146,9 @@ namespace TINK.ViewModel.FindBike
Stations = stations ?? throw new ArgumentException(nameof(stations));
TinkApp = tinkApp
?? throw new ArgumentException("Can not instantiate settings page view model- object. No tink app object available.");
RefreshCommand = new Command(async () => {
IsRefreshing = false;
@ -215,99 +219,136 @@ namespace TINK.ViewModel.FindBike
}
/// <summary> Command object to bind select bike button to view model. </summary>
public System.Windows.Input.ICommand OnSelectBikeRequest => new Xamarin.Forms.Command(async () => await SelectBike(), () => IsSelectBikeEnabled);
public System.Windows.Input.ICommand OnSelectBikeRequest => new Xamarin.Forms.Command(async () => await SelectBike());
/// <summary> Select a bike by ID</summary>
public async Task SelectBike()
{
// Get List of bike to be able to connect to.
ActionText = AppResources.ActivityTextFindBikeLoadingBikes;
IsIdle = false;
IsConnected = IsConnectedDelegate();
Result<BikeCollection> bikes = null;
try
if (!IsSelectBikeEnabled)
{
bikes = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
}
catch (Exception exception)
{
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed (Copri server not reachable).");
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
{
Log.ForContext<FindBikePageViewModel>().Error("Getting bikes failed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
}
ActionText = string.Empty;
IsIdle = true;
await ViewService.DisplayAlert(
String.Empty,
AppResources.ErrorSelectBikeInputNotSufficent,
AppResources.MessageAnswerOk);
return;
}
finally
else
{
Exception = bikes?.Exception ?? null; // Update communication error from query for bikes occupied.
Bikes = bikes.Response;
}
try
{
var selectedBike = Bikes.FirstOrDefault(x => x.Id.Equals(BikeIdUserInput.Trim(), StringComparison.OrdinalIgnoreCase));
// Get List of bike to be able to connect to.
ActionText = AppResources.ActivityTextFindBikeLoadingBikes;
IsIdle = false;
if (selectedBike == null)
IsConnected = IsConnectedDelegate();
Result<BikeCollection> bikes = null;
try
{
await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
string.Format(AppResources.ErrorSelectBikeNoBikeFound, BikeIdUserInput),
AppResources.MessageAnswerOk);
bikes = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
}
catch (Exception exception)
{
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<FindBikePageViewModel>().Information("Getting bikes failed (Copri server not reachable).");
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else
{
Log.ForContext<FindBikePageViewModel>().Error("Getting bikes failed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
}
ActionText = string.Empty;
IsIdle = true;
return;
}
var bikeCollection = new BikeCollection(new Dictionary<string, Model.Bikes.BikeInfoNS.BC.BikeInfo> { { selectedBike.Id, selectedBike } });
var lockIdList = bikeCollection
.GetLockIt()
.Cast<BikeInfo>()
.Select(x => x.LockInfo)
.ToList();
if (LockService is ILocksServiceFake serviceFake)
finally
{
serviceFake.UpdateSimulation(bikeCollection);
Exception = bikes?.Exception ?? null; // Update communication error from query for bikes occupied.
Bikes = bikes.Response;
}
// Check bluetooth and location permission and states
ActionText = AppResources.ActivityTextCheckBluetoothState;
if (bikeCollection.FirstOrDefault(x => x is BikeInfo btBike) != null
//&& RuntimePlatform == Device.Android
)
try
{
// Check location permission
var status = await PermissionsService.CheckStatusAsync();
if (status != Status.Granted)
{
if (RuntimePlatform == Device.Android)
{
var permissionResult = await PermissionsService.RequestAsync();
var selectedBike = Bikes.FirstOrDefault(x => x.Id.Equals(BikeIdUserInput.Trim(), StringComparison.OrdinalIgnoreCase));
if (permissionResult != Status.Granted)
if (selectedBike == null)
{
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
TinkApp.Flavor == AppFlavor.MeinKonrad
? $"{string.Format(AppResources.ErrorSelectBikeNoBikeFound, BikeIdUserInput)}\r\n\r\n{string.Format(AppResources.ErrorSelectBikeNoBikeFoundBikeTypeHint, ActiveFilteredBikeType)}"
: string.Format(AppResources.ErrorSelectBikeNoBikeFound, BikeIdUserInput),
AppResources.MessageAnswerOk);
ActionText = string.Empty;
IsIdle = true;
return;
}
var bikeCollection = new BikeCollection(new Dictionary<string, Model.Bikes.BikeInfoNS.BC.BikeInfo> { { selectedBike.Id, selectedBike } });
var lockIdList = bikeCollection
.GetLockIt()
.Cast<BikeInfo>()
.Select(x => x.LockInfo)
.ToList();
if (LockService is ILocksServiceFake serviceFake)
{
serviceFake.UpdateSimulation(bikeCollection);
}
// Check bluetooth and location permission and states
ActionText = AppResources.ActivityTextCheckBluetoothState;
if (bikeCollection.FirstOrDefault(x => x is BikeInfo btBike) != null
//&& RuntimePlatform == Device.Android
)
{
// Check location permission
var status = await PermissionsService.CheckStatusAsync();
if (status != Status.Granted)
{
if (RuntimePlatform == Device.Android)
{
var permissionResult = await PermissionsService.RequestAsync();
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
if (!dialogResult)
{
// User decided not to give access to locations permissions.
BikeCollection.Update(bikeCollection, Stations);
await StartUpdateTask(() => UpdateTask());
ActionText = string.Empty;
IsIdle = true;
return;
}
// Open permissions dialog.
PermissionsService.OpenAppSettings();
}
}
else
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
@ -331,102 +372,79 @@ namespace TINK.ViewModel.FindBike
PermissionsService.OpenAppSettings();
}
}
else
// Location state
if (GeolocationService.IsGeolcationEnabled == false)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationActivation,
AppResources.MessageAnswerOk);
if (!dialogResult)
{
// User decided not to give access to locations permissions.
BikeCollection.Update(bikeCollection, Stations);
BikeCollection.Update(bikeCollection, Stations);
await StartUpdateTask(() => UpdateTask());
await StartUpdateTask(() => UpdateTask());
ActionText = string.Empty;
IsIdle = true;
return;
}
ActionText = string.Empty;
IsIdle = true;
return;
}
// Open permissions dialog.
PermissionsService.OpenAppSettings();
// Bluetooth state
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
{
await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementBluetoothActivation,
AppResources.MessageAnswerOk);
BikeCollection.Update(bikeCollection, Stations);
await StartUpdateTask(() => UpdateTask());
ActionText = string.Empty;
IsIdle = true;
return;
}
}
// Location state
if (GeolocationService.IsGeolcationEnabled == false)
// Connect to bluetooth devices.
ActionText = AppResources.ActivityTextSearchBikes;
IEnumerable<LockInfoTdo> locksInfoTdo;
try
{
await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementLocationActivation,
AppResources.MessageAnswerOk);
BikeCollection.Update(bikeCollection, Stations);
await StartUpdateTask(() => UpdateTask());
ActionText = string.Empty;
IsIdle = true;
return;
locksInfoTdo = await LockService.GetLocksStateAsync(
lockIdList.Select(x => x.ToLockInfoTdo()).ToList(),
LockService.TimeOut.MultiConnect);
}
catch (Exception exception)
{
Log.ForContext<FindBikePageViewModel>().Error("Getting bluetooth state failed. {Exception}", exception);
locksInfoTdo = new List<LockInfoTdo>();
}
// Bluetooth state
if (await BluetoothService.GetBluetoothState() != BluetoothState.On)
{
await ViewService.DisplayAlert(
AppResources.MessageHintTitle,
AppResources.MessageBikesManagementBluetoothActivation,
AppResources.MessageAnswerOk);
var locksInfo = lockIdList.UpdateById(locksInfoTdo);
BikeCollection.Update(bikeCollection, Stations);
BikeCollection.Update(bikeCollection.UpdateLockInfo(locksInfo), Stations);
await StartUpdateTask(() => UpdateTask());
await StartUpdateTask(() => UpdateTask());
ActionText = string.Empty;
IsIdle = true;
return;
}
}
// Connect to bluetooth devices.
ActionText = AppResources.ActivityTextSearchBikes;
IEnumerable<LockInfoTdo> locksInfoTdo;
try
{
locksInfoTdo = await LockService.GetLocksStateAsync(
lockIdList.Select(x => x.ToLockInfoTdo()).ToList(),
LockService.TimeOut.MultiConnect);
ActionText = string.Empty;
IsIdle = true;
}
catch (Exception exception)
{
Log.ForContext<FindBikePageViewModel>().Error("Getting bluetooth state failed. {Exception}", exception);
locksInfoTdo = new List<LockInfoTdo>();
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
Log.ForContext<FindBikePageViewModel>().Error("Running command to select bike failed. {Exception}", exception);
ActionText = string.Empty;
IsIdle = true;
return;
}
var locksInfo = lockIdList.UpdateById(locksInfoTdo);
BikeCollection.Update(bikeCollection.UpdateLockInfo(locksInfo), Stations);
await StartUpdateTask(() => UpdateTask());
ActionText = string.Empty;
IsIdle = true;
}
catch (Exception exception)
{
await ViewService.DisplayAlert(
AppResources.ErrorSelectBikeTitle,
exception.Message,
AppResources.MessageAnswerOk);
Log.ForContext<FindBikePageViewModel>().Error("Running command to select bike failed. {Exception}", exception);
ActionText = string.Empty;
IsIdle = true;
return;
}
}