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

@ -273,8 +273,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
var feedBackUri = SelectedBike?.OperatorUri;
var battery = SelectedBike.Drive?.Battery;
var feedback = await ViewService.DisplayUserFeedbackPopup(
battery,
bookingFinished?.Co2Saving);
battery);
if (battery != null
&& feedback.CurrentChargeBars != null)
@ -350,11 +349,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
await ViewService.DisplayAlert(
String.Format(AppResources.MessageRentalProcessEndRentalFinishedTitle, SelectedBike.Id),
String.Format(
"{0}{1}",
!string.IsNullOrWhiteSpace(bookingFinished?.Co2Saving) ?
$"{bookingFinished?.Co2Saving}\r\n\r\n"
"{0}{1}{2}{3}{4}",
!string.IsNullOrWhiteSpace(bookingFinished?.Distance) ?
$"{String.Format(AppResources.MessageRentalProcessEndRentalFinishedDistanceText, bookingFinished?.Distance)}\r\n"
: string.Empty,
String.Format(AppResources.MessageRentalProcessEndRentalFinishedText)
!string.IsNullOrWhiteSpace(bookingFinished?.Co2Saving) ?
$"{String.Format(AppResources.MessageRentalProcessEndRentalFinishedCO2SavingText, bookingFinished?.Co2Saving)}\r\n"
: string.Empty,
!string.IsNullOrWhiteSpace(bookingFinished?.Duration) ?
$"{String.Format(AppResources.MessageRentalProcessEndRentalFinishedDurationText, bookingFinished?.Duration)}\r\n"
: $"{string.Empty}",
!string.IsNullOrWhiteSpace(bookingFinished?.RentalCosts) ?
$"{String.Format(AppResources.MessageRentalProcessEndRentalFinishedRentalCostsText,bookingFinished?.RentalCosts)}\r\n"
: $"{AppResources.MessageRentalProcessEndRentalFinishedNoRentalCostsText}\r\n",
AppResources.MessageRentalProcessEndRentalFinishedText
),
AppResources.MessageAnswerOk
);

View file

@ -64,8 +64,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
// Do get Feedback
var battery = SelectedBike.Drive?.Battery;
var feedback = await ViewService.DisplayUserFeedbackPopup(
battery,
SelectedBike?.BookingFinishedModel?.Co2Saving);
battery);
if (battery != null
&& feedback.CurrentChargeBars != null)

View file

@ -6,7 +6,6 @@ using System.Windows.Input;
using Plugin.Messaging;
using Serilog;
using TINK.Model;
using TINK.Model.Stations;
using TINK.Model.Stations.StationNS;
using TINK.MultilingualResources;
using TINK.View;
@ -90,25 +89,23 @@ namespace TINK.ViewModel.Info
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PhoneNumberText)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ProviderNameText)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsOperatorInfoAvaliable)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsDoPhoncallAvailable)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSendMailAvailable)));
await Task.CompletedTask;
}
/// <summary> Command object to bind mail button to view model. </summary>
public ICommand OnMailToOperatorRequest
=> new Command(
async () => await DoSendMailToOperator(),
() => IsSendMailAvailable);
=> new Command(async () => await DoSendMailToOperator());
/// <summary> Command object to bind mail app related button to model. </summary>
public ICommand OnMailAppRelatedRequest
=> new Command(
async () => await DoSendMailAppRelated(),
() => IsSendMailAvailable);
=> new Command(async () => await DoSendMailAppRelated());
/// <summary>True if sending mail is possible.</summary>
public bool IsSendMailAvailable =>
CrossMessaging.Current.EmailMessenger.CanSendEmail;
public bool IsSendMailAvailable
=> CrossMessaging.Current.EmailMessenger.CanSendEmail;
/// <summary>cTrue if doing a phone call is possible.</summary>
@ -134,91 +131,107 @@ namespace TINK.ViewModel.Info
/// <returns> Returns true if either mail was sent or if no mailer available.</returns>
public async Task DoSendMailToOperator()
{
try
if (!IsSendMailAvailable)
{
if (!IsSendMailAvailable)
{
// Nothing to do because email can not be sent.
return;
}
// Send operator related support mail to operator.
await Email.ComposeAsync(new EmailMessage
{
To = new List<string> { MailAddressText },
Cc = APPSUPPORTMAILADDRESS.ToUpper() != MailAddressText.ToUpper() // do not sent copy if same mail address
&& MailAddressText != "konrad@sharee.bike" // do not sent copy if Mein konrad
? new List<string> { APPSUPPORTMAILADDRESS } : new List<string>(),
Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName, SelectedStation?.Id),
Body = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}"
});
return;
}
catch (Exception exception)
{
Log.Error("An unexpected error occurred sending mail to operator. {@Exception}", exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
await ViewService.DisplayAlert(
String.Empty,
AppResources.ErrorSupportmailMailingFailed,
exception.Message,
AppResources.MessageAnswerOk);
return;
}
else
{
try
{
// Send operator related support mail to operator.
await Email.ComposeAsync(new EmailMessage
{
To = new List<string> { MailAddressText },
Cc = APPSUPPORTMAILADDRESS.ToUpper() != MailAddressText.ToUpper() // do not sent copy if same mail address
&& MailAddressText != "konrad@sharee.bike" // do not sent copy if Mein konrad
? new List<string> { APPSUPPORTMAILADDRESS } : new List<string>(),
Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName, SelectedStation?.Id),
Body = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}"
});
return;
}
catch (Exception exception)
{
Log.Error("An unexpected error occurred sending mail to operator. {@Exception}", exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
AppResources.ErrorSupportmailMailingFailed,
exception.Message,
AppResources.MessageAnswerOk);
return;
}
}
}
/// <summary> Request to send a app related mail. </summary>
public async Task DoSendMailAppRelated()
{
try
if (!IsSendMailAvailable)
{
// Ask for permission to append diagnostics.
await ViewService.DisplayAlert(
AppResources.QuestionSupportmailAttachmentTitle,
AppResources.QuestionSupportmailAttachment,
String.Empty,
AppResources.ErrorSupportmailMailingFailed,
AppResources.MessageAnswerOk);
var message = new EmailMessage
{
To = new List<string> { APPSUPPORTMAILADDRESS },
Subject = SelectedStation?.Id != null ? string.Format(AppResources.SupportmailSubjectAppmailWithStation, AppFlavorName, SelectedStation?.Id) : string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName),
Body = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}"
};
// Send with attachment.
var logFileName = string.Empty;
return;
}
else
{
try
{
logFileName = CreateAttachment();
// Ask for permission to append diagnostics.
await ViewService.DisplayAlert(
AppResources.QuestionSupportmailAttachmentTitle,
AppResources.QuestionSupportmailAttachment,
AppResources.MessageAnswerOk);
var message = new EmailMessage
{
To = new List<string> { APPSUPPORTMAILADDRESS },
Subject = SelectedStation?.Id != null ? string.Format(AppResources.SupportmailSubjectAppmailWithStation, AppFlavorName, SelectedStation?.Id) : string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName),
Body = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}"
};
// Send with attachment.
var logFileName = string.Empty;
try
{
logFileName = CreateAttachment();
}
catch (Exception exception)
{
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
AppResources.ErrorSupportmailCreateAttachment,
exception.Message,
AppResources.MessageAnswerOk);
Log.ForContext<ContactPageViewModel>().Error("An error occurred creating attachment for app mail. {@Exception)", exception);
}
if (!string.IsNullOrEmpty(logFileName))
{
message.Attachments.Add(new Xamarin.Essentials.EmailAttachment(logFileName));
}
// Send a tink app related mail
await Email.ComposeAsync(message);
}
catch (Exception exception)
{
Log.ForContext<ContactPageViewModel>().Error("An unexpected error occurred sending mail. {@Exception}", exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
AppResources.ErrorSupportmailCreateAttachment,
AppResources.ErrorSupportmailMailingFailed,
exception.Message,
AppResources.MessageAnswerOk);
Log.ForContext<ContactPageViewModel>().Error("An error occurred creating attachment for app mail. {@Exception)", exception);
return;
}
if (!string.IsNullOrEmpty(logFileName))
{
message.Attachments.Add(new Xamarin.Essentials.EmailAttachment(logFileName));
}
// Send a tink app related mail
await Email.ComposeAsync(message);
}
catch (Exception exception)
{
Log.ForContext<ContactPageViewModel>().Error("An unexpected error occurred sending mail. {@Exception}", exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
AppResources.ErrorSupportmailMailingFailed,
exception.Message,
AppResources.MessageAnswerOk);
return;
}
}
@ -257,31 +270,37 @@ namespace TINK.ViewModel.Info
/// <summary> Command object to bind phone call button. </summary>
public ICommand OnPhoneRequest
=> new Command(
async () => await DoPhoneCall(),
() => IsDoPhoncallAvailable);
=> new Command(async () => await DoPhoneCall());
/// <summary> Request to do a phone call. </summary>
public async Task DoPhoneCall()
{
try
if (!IsDoPhoncallAvailable)
{
// Make Phone Call
if (IsDoPhoncallAvailable)
{
CrossMessaging.Current.PhoneDialer.MakePhoneCall(PhoneNumberText);
}
}
catch (Exception exception)
{
Log.Error("An unexpected error occurred doing a phone call. {@Exception}", exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
await ViewService.DisplayAlert(
String.Empty,
AppResources.ErrorSupportmailPhoningFailed,
exception.Message,
AppResources.MessageAnswerOk);
return;
}
else
{
try
{
// Make Phone Call
CrossMessaging.Current.PhoneDialer.MakePhoneCall(PhoneNumberText);
}
catch (Exception exception)
{
Log.Error("An unexpected error occurred doing a phone call. {@Exception}", exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
AppResources.ErrorSupportmailPhoningFailed,
exception.Message,
AppResources.MessageAnswerOk);
return;
}
}
}
/// <summary> Text providing the id of the selected station.</summary>

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;
}
}

View file

@ -254,157 +254,178 @@ namespace TINK.ViewModel
public async Task Login()
#endif
{
IsIdle = false;
StatusInfoText = AppResources.ActivityTextOneMomentPlease;
IAccount account = new EmptyAccount();
try
if (!IsLoginRequestAllowed)
{
Log.ForContext<LoginPageViewModel>().Information("User taped login button.");
if(TinkApp.ActiveUser.IsLoggedIn)
{
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
string.Format(AppResources.ErrorLoginAlreadyLoggedIn,TinkApp.ActiveUser.Mail),
AppResources.MessageAnswerOk);
}
else if (!m_bMailAndPasswordCandidatesOk)
{
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
AppResources.ErrorLoginInvalidMailOrPasswortInput,
AppResources.MessageAnswerOk);
}
return;
}
else
{
IsIdle = false;
StatusInfoText = AppResources.ActivityTextOneMomentPlease;
IAccount account = new EmptyAccount();
try
{
Log.ForContext<LoginPageViewModel>().Information("User taped login button.");
try
{
TinkApp.ActiveUser.CheckIsPasswordValid(MailAddress, Password);
// Do login.
account = await TinkApp.GetConnector(CrossConnectivity.Current.IsConnected).Command.DoLogin(MailAddress, Password, TinkApp.ActiveUser.DeviceId);
await TinkApp.ActiveUser.Login(account);
// Update map page filter because user might be of both groups TINK an Konrad.
TinkApp.GroupFilterMapPage =
GroupFilterMapPageHelper.CreateUpdated(
TinkApp.GroupFilterMapPage,
TinkApp.ActiveUser.DoFilter(TinkApp.FilterGroupSetting.DoFilter()));
// Update settings page filter because user might be of both groups TINK an Konrad.
TinkApp.FilterGroupSetting.DoFilter(TinkApp.ActiveUser.Group);
// Persist new settings.
TinkApp.Save();
TinkApp.UpdateConnector();
}
catch (InvalidAuthorizationResponseException l_oException)
{
// Copri response is invalid.
Log.ForContext<LoginPageViewModel>().Error("Login failed (invalid. auth. response). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
AppResources.ErrorLoginInvalidAuthorization,
AppResources.MessageAnswerOk);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
catch (UnsupportedCopriVersionDetectedException)
{
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
string.Format(
AppResources.MessageAppVersionIsOutdated,
TinkApp.Flavor.GetDisplayName()),
AppResources.MessageAnswerOk);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
catch (Exception l_oException)
{
// Copri server is not reachable.
if (l_oException is WebConnectFailureException)
{
Log.ForContext<LoginPageViewModel>().Information("Login failed (web communication exception). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else if (l_oException is UsernamePasswordInvalidException)
{
// Cookie is empty.
Log.ForContext<LoginPageViewModel>().Error("Login failed (empty cookie). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
string.Format(AppResources.ErrorLoginNoCookie, l_oException.Message),
AppResources.MessageAnswerOk);
}
else
{
Log.ForContext<LoginPageViewModel>().Error("Login failed. {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
l_oException.Message,
AppResources.MessageAnswerOk);
}
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
// Display information that login succeeded.
Log.ForContext<LoginPageViewModel>().Information("Login succeeded. {@tinkApp.ActiveUser}.", TinkApp.ActiveUser);
var title = TinkApp.ActiveUser.Group.Intersect(new List<string> { Model.Connector.FilterHelper.CARGOBIKE, Model.Connector.FilterHelper.CITYBIKE }).Any()
? string.Format(AppResources.MessageLoginWelcomeTitleGroup, TinkApp.ActiveUser.GetUserGroupDisplayName())
: string.Format(AppResources.MessageLoginWelcomeTitle);
await m_oViewService.DisplayAlert(
title,
string.Format(AppResources.MessageLoginWelcome, TinkApp.ActiveUser.Mail),
AppResources.MessageAnswerOk);
}
catch (Exception p_oException)
{
Log.ForContext<LoginPageViewModel>().Error("An unexpected error occurred displaying log out page. {@Exception}", p_oException);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
try
{
TinkApp.ActiveUser.CheckIsPasswordValid(MailAddress, Password);
// Do login.
account = await TinkApp.GetConnector(CrossConnectivity.Current.IsConnected).Command.DoLogin(MailAddress, Password, TinkApp.ActiveUser.DeviceId);
await TinkApp.ActiveUser.Login(account);
// Update map page filter because user might be of both groups TINK an Konrad.
TinkApp.GroupFilterMapPage =
GroupFilterMapPageHelper.CreateUpdated(
TinkApp.GroupFilterMapPage,
TinkApp.ActiveUser.DoFilter(TinkApp.FilterGroupSetting.DoFilter()));
// Update settings page filter because user might be of both groups TINK an Konrad.
TinkApp.FilterGroupSetting.DoFilter(TinkApp.ActiveUser.Group);
// Persist new settings.
TinkApp.Save();
TinkApp.UpdateConnector();
}
catch (InvalidAuthorizationResponseException l_oException)
{
// Copri response is invalid.
Log.ForContext<LoginPageViewModel>().Error("Login failed (invalid. auth. response). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
l_oException.Message,
AppResources.MessageAnswerOk);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
catch (UnsupportedCopriVersionDetectedException)
{
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
string.Format(
AppResources.MessageAppVersionIsOutdated,
TinkApp.Flavor.GetDisplayName()),
AppResources.MessageAnswerOk);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
catch (Exception l_oException)
{
// Copri server is not reachable.
if (l_oException is WebConnectFailureException)
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.CARGOBIKE))
{
Log.ForContext<LoginPageViewModel>().Information("Login failed (web communication exception). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else if (l_oException is UsernamePasswordInvalidException)
{
// Cookie is empty.
Log.ForContext<LoginPageViewModel>().Error("Login failed (empty cookie). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
string.Format(AppResources.ErrorLoginNoCookie, l_oException.Message),
"OK");
}
else
{
Log.ForContext<LoginPageViewModel>().Error("Login failed. {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
l_oException.Message,
AppResources.MessageAnswerOk);
}
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
// Display information that login succeeded.
Log.ForContext<LoginPageViewModel>().Information("Login succeeded. {@tinkApp.ActiveUser}.", TinkApp.ActiveUser);
var title = TinkApp.ActiveUser.Group.Intersect(new List<string> { Model.Connector.FilterHelper.CARGOBIKE, Model.Connector.FilterHelper.CITYBIKE }).Any()
? string.Format(AppResources.MessageLoginWelcomeTitleGroup, TinkApp.ActiveUser.GetUserGroupDisplayName())
: string.Format(AppResources.MessageLoginWelcomeTitle);
await m_oViewService.DisplayAlert(
title,
string.Format(AppResources.MessageLoginWelcome, TinkApp.ActiveUser.Mail),
AppResources.MessageAnswerOk);
}
catch (Exception p_oException)
{
Log.ForContext<LoginPageViewModel>().Error("An unexpected error occurred displaying log out page. {@Exception}", p_oException);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
try
{
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.CARGOBIKE))
{
// No need to show "Anleitung TINK Räder" because user can not use tink.
// No need to show "Anleitung TINK Räder" because user can not use tink.
#if USEFLYOUT
m_oViewService.ShowPage(account.IsAgbAcknowledged ? ViewTypes.MapPage : ViewTypes.ManageAccountPage);
#else
await m_oViewService.ShowPage("//MapPage");
#endif
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
// Switch to map page
#if USEFLYOUT
m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions);
#else
await m_oViewService.ShowPage("//MapPage");
#endif
}
catch (Exception p_oException)
{
Log.ForContext<LoginPageViewModel>().Error("Ein unerwarteter Fehler ist auf der Seite Anleitung TINK Räder (nach Anmeldeseite) aufgetreten. {@Exception}", p_oException);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
// Switch to map page
#if USEFLYOUT
m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions);
#else
await m_oViewService.ShowPage("//MapPage");
#endif
}
catch (Exception p_oException)
{
Log.ForContext<LoginPageViewModel>().Error("Ein unerwarteter Fehler ist auf der Seite Anleitung TINK Räder (nach Anmeldeseite) aufgetreten. {@Exception}", p_oException);
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
#if BACKSTYLE
// Navigate back to map page.
await m_oNavigation.PopToRootAsync();
#endif
IsIdle = true;
StatusInfoText = string.Empty;
IsIdle = true;
StatusInfoText = string.Empty;
}
}
/// <summary> Holds whether TINK/ Copri info is shown.</summary>