Closing lock and returning bike speeded up.

This commit is contained in:
Oliver Hauff 2021-08-28 10:04:10 +02:00
parent e4adeb908c
commit db9c288584
70 changed files with 933 additions and 902 deletions

View file

@ -425,6 +425,10 @@ namespace TINK.Model
{
new Version(3, 0, 243),
AppResources.ChangeLog3_0_243
},
{
new Version(3, 0, 244),
AppResources.ChangeLog3_0_231 // Minor improvements.
}
};

View file

@ -312,6 +312,24 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Can not query location info..
/// </summary>
public static string ActivityTextErrorQueryLocationQuery {
get {
return ResourceManager.GetString("ActivityTextErrorQueryLocationQuery", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No location info available..
/// </summary>
public static string ActivityTextErrorQueryLocationWhenAny {
get {
return ResourceManager.GetString("ActivityTextErrorQueryLocationWhenAny", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Battery status cannot be read..
/// </summary>
@ -438,6 +456,33 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Query location....
/// </summary>
public static string ActivityTextQueryLocation {
get {
return ResourceManager.GetString("ActivityTextQueryLocation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cancel query location....
/// </summary>
public static string ActivityTextQueryLocationCancelWait {
get {
return ResourceManager.GetString("ActivityTextQueryLocationCancelWait", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Start query location....
/// </summary>
public static string ActivityTextQueryLocationStart {
get {
return ResourceManager.GetString("ActivityTextQueryLocationStart", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reading charging level....
/// </summary>
@ -456,6 +501,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Asking for permissions....
/// </summary>
public static string ActivityTextRequestingLocationPermissions {
get {
return ResourceManager.GetString("ActivityTextRequestingLocationPermissions", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reserving bike....
/// </summary>
@ -465,6 +519,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Returning bike....
/// </summary>
public static string ActivityTextReturningBike {
get {
return ResourceManager.GetString("ActivityTextReturningBike", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Searching locks....
/// </summary>
@ -779,6 +842,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Closing lock and returning bike speeded up..
/// </summary>
public static string ChangeLog3_0_244 {
get {
return ResourceManager.GetString("ChangeLog3_0_244", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lock of rented bike can not be found..
/// </summary>
@ -965,6 +1037,24 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Internet must be available when returning the bike..
/// </summary>
public static string ErrorReturnBikeNoWebMessage {
get {
return ResourceManager.GetString("ErrorReturnBikeNoWebMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connection error when returning the bike!.
/// </summary>
public static string ErrorReturnBikeNoWebTitle {
get {
return ResourceManager.GetString("ErrorReturnBikeNoWebTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Your feedback could not be send to server successfully..
/// </summary>
@ -1514,6 +1604,33 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Closing the lock and ending the rental is not possible..
/// </summary>
public static string MessageErrorQueryLocationMessage {
get {
return ResourceManager.GetString("MessageErrorQueryLocationMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error Start Query Location!.
/// </summary>
public static string MessageErrorQueryLocationStartTitle {
get {
return ResourceManager.GetString("MessageErrorQueryLocationStartTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error Query Location!.
/// </summary>
public static string MessageErrorQueryLocationTitle {
get {
return ResourceManager.GetString("MessageErrorQueryLocationTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Login cookie must not be empty. {0}.
/// </summary>
@ -1607,15 +1724,6 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Rent bike {0} and open lock?.
/// </summary>
public static string MessageOpenLockAndBookeBike {
get {
return ResourceManager.GetString("MessageOpenLockAndBookeBike", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Urgent questions?.
/// </summary>
@ -1733,6 +1841,24 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Close lock and return bike {0}?.
/// </summary>
public static string QuestionCloseLockAndReturnBike {
get {
return ResourceManager.GetString("QuestionCloseLockAndReturnBike", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Rent bike {0} and open lock?.
/// </summary>
public static string QuestionOpenLockAndBookBike {
get {
return ResourceManager.GetString("QuestionOpenLockAndBookBike", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reserve bike {0} free of charge for {1} min?.
/// </summary>
@ -1742,6 +1868,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Return bike {0}?.
/// </summary>
public static string QuestionReturnBike {
get {
return ResourceManager.GetString("QuestionReturnBike", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} app request.
/// </summary>

View file

@ -374,9 +374,6 @@ Freigabedialog öffen?</value>
Software Pakete aktualisiert.
Zielplatform Android 11.</value>
</data>
<data name="MessageOpenLockAndBookeBike" xml:space="preserve">
<value>Fahrrad {0} mieten und Schloss öffnen?</value>
</data>
<data name="ActivityTextReservingBike" xml:space="preserve">
<value>Reserviere Rad...</value>
</data>
@ -653,4 +650,53 @@ Layout Anzeige Radnamen und nummern verbessert.</value>
<value>Miniumfrage implementiert.
Kleinere Verbesserungen.</value>
</data>
<data name="ActivityTextRequestingLocationPermissions" xml:space="preserve">
<value>Anfrage nach Berechtigungen...</value>
</data>
<data name="ActivityTextQueryLocation" xml:space="preserve">
<value>Abfrage Standort...</value>
</data>
<data name="MessageErrorQueryLocationTitle" xml:space="preserve">
<value>Fehler bei Standortabfrage!</value>
</data>
<data name="MessageErrorQueryLocationMessage" xml:space="preserve">
<value>Schloss schließen und Miete beenden ist nicht möglich.</value>
</data>
<data name="MessageErrorQueryLocationStartTitle" xml:space="preserve">
<value>Fehler beim Start der Standortabfrage!</value>
</data>
<data name="ActivityTextErrorQueryLocationWhenAny" xml:space="preserve">
<value>Keine Standortinfo verfügbar.</value>
</data>
<data name="ActivityTextErrorQueryLocationQuery" xml:space="preserve">
<value>Standortabfrage nicht möglich.</value>
</data>
<data name="ActivityTextQueryLocationCancelWait" xml:space="preserve">
<value>Abbruch Standortabfrage...</value>
</data>
<data name="ActivityTextQueryLocationStart" xml:space="preserve">
<value>Start Standortabfrage...</value>
</data>
<data name="QuestionCloseLockAndReturnBike" xml:space="preserve">
<value>Fahrrad {0} abschließen und zurückgeben?</value>
</data>
<data name="QuestionReturnBike" xml:space="preserve">
<value>Fahrrad {0} zurückgeben?
</value>
</data>
<data name="ActivityTextReturningBike" xml:space="preserve">
<value>Gebe Rad zurück...</value>
</data>
<data name="QuestionOpenLockAndBookBike" xml:space="preserve">
<value>Fahrrad {0} mieten und Schloss öffnen?</value>
</data>
<data name="ErrorReturnBikeNoWebMessage" xml:space="preserve">
<value>Internet muss erreichbar sein beim Zurückgeben des Rads.</value>
</data>
<data name="ErrorReturnBikeNoWebTitle" xml:space="preserve">
<value>Verbingungsfehler beim Zurückgeben des Rads!</value>
</data>
<data name="ChangeLog3_0_244" xml:space="preserve">
<value>Abschließen von Rad und Radrückgabe beschleunigt.</value>
</data>
</root>

View file

@ -482,7 +482,7 @@ Targets Android 11.</value>
<data name="ActivityTextReservingBike" xml:space="preserve">
<value>Reserving bike...</value>
</data>
<data name="MessageOpenLockAndBookeBike" xml:space="preserve">
<data name="QuestionOpenLockAndBookBike" xml:space="preserve">
<value>Rent bike {0} and open lock?</value>
</data>
<data name="ActivityTextErrorReadingChargingLevelGeneral" xml:space="preserve">
@ -749,4 +749,49 @@ Layout of bike names and id display improved.</value>
<value>Mini survey implemented.
Minor fixes.</value>
</data>
<data name="ActivityTextRequestingLocationPermissions" xml:space="preserve">
<value>Asking for permissions...</value>
</data>
<data name="ActivityTextErrorQueryLocationQuery" xml:space="preserve">
<value>Can not query location info.</value>
</data>
<data name="ActivityTextErrorQueryLocationWhenAny" xml:space="preserve">
<value>No location info available.</value>
</data>
<data name="ActivityTextQueryLocation" xml:space="preserve">
<value>Query location...</value>
</data>
<data name="ActivityTextQueryLocationCancelWait" xml:space="preserve">
<value>Cancel query location...</value>
</data>
<data name="ActivityTextQueryLocationStart" xml:space="preserve">
<value>Start query location...</value>
</data>
<data name="MessageErrorQueryLocationMessage" xml:space="preserve">
<value>Closing the lock and ending the rental is not possible.</value>
</data>
<data name="MessageErrorQueryLocationStartTitle" xml:space="preserve">
<value>Error Start Query Location!</value>
</data>
<data name="MessageErrorQueryLocationTitle" xml:space="preserve">
<value>Error Query Location!</value>
</data>
<data name="QuestionCloseLockAndReturnBike" xml:space="preserve">
<value>Close lock and return bike {0}?</value>
</data>
<data name="ActivityTextReturningBike" xml:space="preserve">
<value>Returning bike...</value>
</data>
<data name="QuestionReturnBike" xml:space="preserve">
<value>Return bike {0}?</value>
</data>
<data name="ChangeLog3_0_244" xml:space="preserve">
<value>Closing lock and returning bike speeded up.</value>
</data>
<data name="ErrorReturnBikeNoWebMessage" xml:space="preserve">
<value>Internet must be available when returning the bike.</value>
</data>
<data name="ErrorReturnBikeNoWebTitle" xml:space="preserve">
<value>Connection error when returning the bike!</value>
</data>
</root>

View file

@ -496,10 +496,6 @@ Targets Android 11.</source>
Software Pakete aktualisiert.
Zielplatform Android 11.</target>
</trans-unit>
<trans-unit id="MessageOpenLockAndBookeBike" translate="yes" xml:space="preserve">
<source>Rent bike {0} and open lock?</source>
<target state="translated">Fahrrad {0} mieten und Schloss öffnen?</target>
</trans-unit>
<trans-unit id="ActivityTextReservingBike" translate="yes" xml:space="preserve">
<source>Reserving bike...</source>
<target state="translated">Reserviere Rad...</target>
@ -875,6 +871,71 @@ Minor fixes.</source>
<target state="translated">Miniumfrage implementiert.
Kleinere Verbesserungen.</target>
</trans-unit>
<trans-unit id="ActivityTextRequestingLocationPermissions" translate="yes" xml:space="preserve">
<source>Asking for permissions...</source>
<target state="translated">Anfrage nach Berechtigungen...</target>
</trans-unit>
<trans-unit id="ActivityTextQueryLocation" translate="yes" xml:space="preserve">
<source>Query location...</source>
<target state="translated">Abfrage Standort...</target>
</trans-unit>
<trans-unit id="MessageErrorQueryLocationTitle" translate="yes" xml:space="preserve">
<source>Error Query Location!</source>
<target state="translated">Fehler bei Standortabfrage!</target>
</trans-unit>
<trans-unit id="MessageErrorQueryLocationMessage" translate="yes" xml:space="preserve">
<source>Closing the lock and ending the rental is not possible.</source>
<target state="translated">Schloss schließen und Miete beenden ist nicht möglich.</target>
</trans-unit>
<trans-unit id="MessageErrorQueryLocationStartTitle" translate="yes" xml:space="preserve">
<source>Error Start Query Location!</source>
<target state="translated">Fehler beim Start der Standortabfrage!</target>
</trans-unit>
<trans-unit id="ActivityTextErrorQueryLocationWhenAny" translate="yes" xml:space="preserve">
<source>No location info available.</source>
<target state="translated">Keine Standortinfo verfügbar.</target>
</trans-unit>
<trans-unit id="ActivityTextErrorQueryLocationQuery" translate="yes" xml:space="preserve">
<source>Can not query location info.</source>
<target state="translated">Standortabfrage nicht möglich.</target>
</trans-unit>
<trans-unit id="ActivityTextQueryLocationCancelWait" translate="yes" xml:space="preserve">
<source>Cancel query location...</source>
<target state="translated">Abbruch Standortabfrage...</target>
</trans-unit>
<trans-unit id="ActivityTextQueryLocationStart" translate="yes" xml:space="preserve">
<source>Start query location...</source>
<target state="translated">Start Standortabfrage...</target>
</trans-unit>
<trans-unit id="QuestionCloseLockAndReturnBike" translate="yes" xml:space="preserve">
<source>Close lock and return bike {0}?</source>
<target state="translated">Fahrrad {0} abschließen und zurückgeben?</target>
</trans-unit>
<trans-unit id="QuestionReturnBike" translate="yes" xml:space="preserve">
<source>Return bike {0}?</source>
<target state="translated">Fahrrad {0} zurückgeben?
</target>
</trans-unit>
<trans-unit id="ActivityTextReturningBike" translate="yes" xml:space="preserve">
<source>Returning bike...</source>
<target state="translated">Gebe Rad zurück...</target>
</trans-unit>
<trans-unit id="QuestionOpenLockAndBookBike" translate="yes" xml:space="preserve">
<source>Rent bike {0} and open lock?</source>
<target state="translated">Fahrrad {0} mieten und Schloss öffnen?</target>
</trans-unit>
<trans-unit id="ErrorReturnBikeNoWebMessage" translate="yes" xml:space="preserve">
<source>Internet must be available when returning the bike.</source>
<target state="translated">Internet muss erreichbar sein beim Zurückgeben des Rads.</target>
</trans-unit>
<trans-unit id="ErrorReturnBikeNoWebTitle" translate="yes" xml:space="preserve">
<source>Connection error when returning the bike!</source>
<target state="translated">Verbingungsfehler beim Zurückgeben des Rads!</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_244" translate="yes" xml:space="preserve">
<source>Closing lock and returning bike speeded up.</source>
<target state="translated">Abschließen von Rad und Radrückgabe beschleunigt.</target>
</trans-unit>
</group>
</body>
</file>

View file

@ -1,5 +1,6 @@
using Serilog;
using System;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Device;
using Xamarin.Essentials;
@ -22,13 +23,17 @@ namespace TINK.Model.Services.Geolocation
public bool IsGeolcationEnabled => Dependent.IsGeolcationEnabled;
/// <summary> Gets the current location.</summary>
/// <param name="cancellationToken">Token to cancel request for geolocation.</param>
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine whether cached geoloation can be used or not.</param>
public async Task<Location> GetAsync(DateTime? timeStamp = null)
public async Task<Location> GetAsync(CancellationToken? cancellationToken = null, DateTime? timeStamp = null)
{
try
{
var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromMilliseconds(GEOLOCATIONREQUEST_TIMEOUT_MS));
return await Xamarin.Essentials.Geolocation.GetLocationAsync(request);
return cancellationToken.HasValue
? await Xamarin.Essentials.Geolocation.GetLocationAsync(request, cancellationToken.Value)
: await Xamarin.Essentials.Geolocation.GetLocationAsync(request);
}
catch (FeatureNotSupportedException fnsEx)
{

View file

@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Device;
using Xamarin.Essentials;
@ -9,9 +10,10 @@ namespace TINK.Model.Services.Geolocation
public interface IGeolocation : IGeolodationDependent
{
/// <summary> Gets the current location.</summary>
/// <param name="cancellationToken">Token to cancel request for geolocation. If null request can not be cancels and times out after GeolocationService.GEOLOCATIONREQUEST_TIMEOUT_MS if geolocation is not available.</param>
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine for some implementations whether cached geoloation can be used or not.</param>
/// <returns></returns>
Task<Location> GetAsync(DateTime? timeStamp = null);
Task<Location> GetAsync(CancellationToken? cancellationToken = null, DateTime? timeStamp = null);
/// <summary> If true location data returned is simulated.</summary>
bool IsSimulation { get; }

View file

@ -1,5 +1,6 @@
using Serilog;
using System;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Device;
using Xamarin.Essentials;
@ -8,24 +9,22 @@ namespace TINK.Model.Services.Geolocation
{
public class LastKnownGeolocationService : IGeolocation
{
/// <summary> Timeout for geolocation request operations.</summary>
private const int GEOLOCATIONREQUEST_TIMEOUT_MS = 5000;
private IGeolodationDependent Dependent { get; }
public LastKnownGeolocationService(IGeolodationDependent dependent)
{
Dependent = dependent;
}
/// <summary> Gets the current location.</summary>
/// <param name="cancelationToken">Token to cancel request for geolocation.</param>
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine whether cached geoloation can be used or not.</param>
public async Task<Location> GetAsync(DateTime? timeStamp = null)
public async Task<Location> GetAsync(CancellationToken? cancelationToken = null, DateTime? timeStamp = null)
{
Location location;
try
{
var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromMilliseconds(GEOLOCATIONREQUEST_TIMEOUT_MS));
location = await Xamarin.Essentials.Geolocation.GetLocationAsync(request);
location = await Xamarin.Essentials.Geolocation.GetLastKnownLocationAsync();
}
catch (FeatureNotSupportedException fnsEx)
{
@ -59,13 +58,14 @@ namespace TINK.Model.Services.Geolocation
return location;
}
return await Xamarin.Essentials.Geolocation.GetLocationAsync();
return await new GeolocationService(Dependent).GetAsync(cancelationToken, timeStamp);
}
/// <summary> If true location data returned is simulated.</summary>
public bool IsSimulation { get => false; }
public TimeSpan MaxAge => new TimeSpan(0, 3, 0);
/// <summary> Maximum age allowed for location info. </summary>
public TimeSpan MaxAge => new TimeSpan(0, 3 /*minutes*/, 0);
public bool IsGeolcationEnabled => Dependent.IsGeolcationEnabled;
}

View file

@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Device;
using Xamarin.Essentials;
@ -14,7 +15,10 @@ namespace TINK.Model.Services.Geolocation
Dependent = dependent;
}
public async Task<Location> GetAsync(DateTime? timeStamp = null)
/// <summary> Gets the current location.</summary>
/// <param name="cancelToken">Token to cancel request for geolocation.</param>
/// <param name="timeStamp">Time when geolocation is of interest. Is used to determine whether cached geoloation can be used or not.</param>
public async Task<Location> GetAsync(CancellationToken? cancelToken = null, DateTime? timeStamp = null)
{
return await Task.FromResult(new Location(47.976634, 7.825490) { Accuracy = 0, Timestamp = timeStamp ?? DateTime.Now }); ;
}

View file

@ -51,7 +51,7 @@ namespace TINK.View
/// <returns>T</returns>
Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary> Shows a page.</summary>
/// <param name="type">Type of page to show.</param>
/// <param name="title">Title of page to show.</param>

View file

@ -1,4 +1,4 @@
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using Serilog;
using System;

View file

@ -1,4 +1,4 @@
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
namespace TINK.View

View file

@ -1,4 +1,4 @@
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using System;
namespace TINK.View.MasterDetail

View file

@ -325,7 +325,7 @@ namespace TINK.ViewModel.Account
try
{
// Switch to map view after log out.
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
m_oViewService.ShowPage(ViewTypes.MapPage);
#else
await m_oViewService.ShowPage("//MapPage");

View file

@ -67,7 +67,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
try
{
// Switch to login page
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
ViewService.ShowPage(ViewTypes.LoginPage);
#else
await ViewService.ShowPage("//LoginPage");

View file

@ -16,6 +16,8 @@ using Xamarin.Essentials;
using TINK.Repository.Request;
using TINK.Model.Device;
using TINK.Model.MiniSurvey;
using System.Collections.Generic;
using System.Threading;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
@ -64,18 +66,54 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
/// <summary> Return bike. </summary>
public async Task<IRequestHandler> ReturnBike()
{
BikesViewModel.IsIdle = false;
BikesViewModel.IsIdle = false;
var ctsLocation = new CancellationTokenSource();
Task<Location> currentLocationTask = null;
var timeStamp = DateTime.Now;
// Check if bike is around.
var deviceState = LockService[SelectedBike.LockInfo.Id].GetDeviceState();
if (deviceState == DeviceState.Connected)
{
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart; // Bluetooth is in reach
// Start getting geolocation.
try
{
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedClosed>().Information("Getting geolocation when returning bike {Bike} failed. {Exception}", SelectedBike, ex);
}
}
// Ask whether to really return bike?
var l_oResult = await ViewService.DisplayAlert(
string.Empty,
$"Fahrrad {SelectedBike.GetFullDisplayName()} zurückgeben?",
"Ja",
"Nein");
string.Format(AppResources.QuestionReturnBike, SelectedBike.GetFullDisplayName()),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
if (l_oResult == false)
{
// User aborted returning bike process
Log.ForContext<BookedClosed>().Information("User selected booked bike {l_oId} in order to return but action was canceled.", SelectedBike.Id);
// Cancel getting geolocation.
ctsLocation.Cancel();
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
try
{
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedClosed>().Information("Canceling query location failed on abort returning closed bike failed. {Exception}", SelectedBike, ex);
}
BikesViewModel.IsIdle = true;
return this;
}
@ -88,23 +126,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewUpdateManager().StopUpdatePeridically();
// Check if bike is around.
Location currentLocation = null;
LocationDto currentLocationDto = null;
var deviceState = LockService[SelectedBike.LockInfo.Id].GetDeviceState();
if (deviceState == DeviceState.Connected)
{
// Bluetooth is in reach
// Get geoposition to pass when returning.
var timeStamp = DateTime.Now;
BikesViewModel.ActionText = "Abfrage Standort...";
Location currentLocation = null;
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
try
{
currentLocation = await Geolocation.GetAsync(timeStamp);
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
currentLocation = currentLocationTask?.Result ?? null;
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedClosed>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
Log.ForContext<BookedClosed>().Information("Returning closed bike {Bike} is not possible. Cancel geolocation query failed. {Exception}", SelectedBike, ex);
}
currentLocationDto = currentLocation != null
@ -123,7 +158,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
SelectedBike.LockInfo.State = LockingState.Disconnected;
}
BikesViewModel.ActionText = "Gebe Rad zurück...";
BikesViewModel.ActionText = "Returning bike...";
IsConnected = IsConnectedDelegate();
var feedBackUri = SelectedBike?.OperatorUri;
@ -146,10 +181,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Copri server is not reachable.
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
await ViewService.DisplayAlert(
"Verbingungsfehler beim Zurückgeben des Rads!",
string.Format("{0}\r\n{1}\r\n{2}", "Internet muss erreichbar sein zum Zurückgeben des Rads.", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
"OK");
await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle,
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
exception.Message,
AppResources.MessageAnswerOk);
}
else if (exception is NotAtStationException notAtStationException)
{
@ -159,7 +195,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeNotAtStationTitle,
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is NoGPSDataException)
{
@ -169,7 +205,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeNotAtStationTitle,
string.Format(AppResources.ErrorReturnBikeLockClosedNoGPSMessage),
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is ResponseException copriException)
{
@ -188,7 +224,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
"Fehler beim Zurückgeben des Rads!",
exception.Message, "OK");
exception.Message,
"OK");
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
@ -208,7 +245,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
catch (Exception exception)
{
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
Log.ForContext<BookedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
}
@ -230,11 +267,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
if (exception is ResponseException copriException)
{
// Copri server is not reachable.
Log.ForContext<BookedOpen>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
Log.ForContext<BookedClosed>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
}
else
{
Log.ForContext<BookedOpen>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
Log.ForContext<BookedClosed>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
}
await ViewService.DisplayAlert(
@ -314,7 +351,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
&& inconsistentState.State == LockingState.Closed)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockTitle,

View file

@ -93,7 +93,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
if (l_oException is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<DisposableDisconnected>().Information("User selected booked bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
Log.ForContext<BookedDisconnected>().Information("User selected booked bike {l_oId} to connect to lock. (Copri server not reachable).", SelectedBike.Id);
await ViewService.DisplayAlert(
"Fehler bei Verbinden mit Schloss!",
@ -102,7 +102,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else
{
Log.ForContext<DisposableDisconnected>().Error("User selected booked bike {l_oId} to connect to lock. {@l_oException}", SelectedBike.Id, l_oException);
Log.ForContext<BookedDisconnected>().Error("User selected booked bike {l_oId} to connect to lock. {@l_oException}", SelectedBike.Id, l_oException);
await ViewService.DisplayAlert(
"Fehler bei Verbinden mit Schloss!",

View file

@ -16,6 +16,8 @@ using TINK.Model.User;
using TINK.Repository.Request;
using TINK.Model.Device;
using TINK.Model.MiniSurvey;
using System.Collections.Generic;
using System.Threading;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
@ -62,18 +64,62 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
/// <summary> Close lock and return bike.</summary>
public async Task<IRequestHandler> CloseLockAndReturnBike()
{
// Ask whether to really return bike?
// Prevent concurrent interaction
BikesViewModel.IsIdle = false;
// Start getting geolocation.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
var ctsLocation = new CancellationTokenSource();
Task<Location> currentLocationTask = null;
var timeStamp = DateTime.Now;
try
{
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
AppResources.MessageErrorQueryLocationStartTitle,
$"{AppResources.MessageErrorQueryLocationMessage}\r\n{ex.Message}",
AppResources.MessageAnswerOk);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true; // Unlock GUI
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
}
// Ask whether to really return bike?
var l_oResult = await ViewService.DisplayAlert(
string.Empty,
$"Fahrrad {SelectedBike.GetFullDisplayName()} abschließen und zurückgeben?",
"Ja",
"Nein");
string.Format(AppResources.QuestionCloseLockAndReturnBike, SelectedBike.GetFullDisplayName()),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
if (l_oResult == false)
{
// User aborted closing and returning bike process
Log.ForContext<BookedOpen>().Information("User selected booked bike {l_oId} in order to close and return but action was canceled.", SelectedBike.Id);
// Cancel getting geolocation.
ctsLocation.Cancel();
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
try
{
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Canceling query location failed on abort returning opened bike failed. {Exception}", SelectedBike, ex);
}
BikesViewModel.IsIdle = true;
return this;
}
@ -85,6 +131,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
// Close lock
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
try
{
@ -94,6 +141,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
BikesViewModel.ActionText = string.Empty;
// Signal cts to cancel getting geolocation.
ctsLocation.Cancel();
if (exception is OutOfReachException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
@ -101,7 +151,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
AppResources.ErrorCloseLockOutOfReachMessage,
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is CounldntCloseMovingException)
{
@ -110,7 +160,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
AppResources.ErrorCloseLockMovingMessage,
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is CouldntCloseBoldBlockedException)
{
@ -119,7 +169,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
AppResources.ErrorCloseLockBoldBlockedMessage,
"OK");
AppResources.MessageAnswerOk);
}
else
{
@ -128,13 +178,25 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
exception.Message,
"OK");
AppResources.MessageAnswerOk);
}
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
? stateAwareException.State
: LockingState.Disconnected;
// Wait until cancel getting geolocation has completed.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
try
{
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
@ -146,14 +208,29 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
if (SelectedBike.LockInfo.State != LockingState.Closed)
{
Log.ForContext<BookedOpen>().Error($"Lock can not be closed. Invalid locking state state {SelectedBike.LockInfo.State} detected.");
// Signal cts to cancel getting geolocation.
ctsLocation.Cancel();
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
SelectedBike.LockInfo.State == LockingState.Open
? AppResources.ErrorCloseLockStillOpenMessage
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
"OK");
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
AppResources.MessageAnswerOk);
// Wait until cancel getting geolocation has completed.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
try
{
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Canceling query location failed on unexpected lock state failed. {Exception}", SelectedBike, ex);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
@ -163,24 +240,24 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
}
// Get geoposition.
var timeStamp = DateTime.Now;
BikesViewModel.ActionText = "Abfrage Standort...";
Location currentLocation;
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
Location currentLocation = null;
try
{
currentLocation = await Geolocation.GetAsync(timeStamp);
var task = await Task.WhenAny(new List<Task> { currentLocationTask });
currentLocation = currentLocationTask.Result;
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Query location failed. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = string.Empty;
await ViewService.DisplayAlert(
"Fehler bei Standortabfrage!",
string.Format($"Schloss schließen und Miete beenden ist nicht möglich.\r\n{ex.Message}"),
"OK");
await ViewService.DisplayAdvancedAlert(
AppResources.MessageErrorQueryLocationTitle,
AppResources.MessageErrorQueryLocationMessage,
ex.GetErrorMessage(),
AppResources.MessageAnswerOk);
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
@ -191,7 +268,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
// Lock list to avoid multiple taps while copri action is pending.
BikesViewModel.ActionText = "Gebe Rad zurück...";
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
IsConnected = IsConnectedDelegate();
@ -222,10 +299,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Copri server is not reachable.
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
await ViewService.DisplayAlert(
"Verbingungsfehler beim Zurückgeben des Rads!",
string.Format("{0}\r\n{1}\r\n{2}", "Internet muss erreichbar sein beim Zurückgeben des Rads.", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
"OK");
await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle,
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
exception.Message,
AppResources.MessageAnswerOk);
}
else if (exception is NotAtStationException notAtStationException)
{
@ -235,7 +313,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeNotAtStationTitle,
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is NoGPSDataException)
{
@ -245,7 +323,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeNotAtStationTitle,
string.Format(AppResources.ErrorReturnBikeLockOpenNoGPSMessage),
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is ResponseException copriException)
{
@ -256,7 +334,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
"Statusfehler beim Zurückgeben des Rads!",
copriException.Message,
copriException.Response,
"OK");
AppResources.MessageAnswerOk);
}
else
{
@ -283,7 +361,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
catch (Exception exception)
{
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
Log.ForContext<BookedOpen>().Error("Lock can not be disconnected. {Exception}", exception);
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
}
@ -345,12 +423,29 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
BikesViewModel.IsIdle = false;
Log.ForContext<BookedOpen>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
// Start getting geolocation.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
var ctsLocation = new CancellationTokenSource();
Task<Location> currentLocationTask = null;
var timeStamp = DateTime.Now;
try
{
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Closing lock of bike {Bike} is not possible. Starting query location failed. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
}
// Stop polling before returning bike.
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
// Close lock
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
try
{
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.Disconnected;
@ -359,13 +454,16 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
BikesViewModel.ActionText = string.Empty;
// Signal cts to cancel getting geolocation.
ctsLocation.Cancel();
if (exception is OutOfReachException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
AppResources.ErrorCloseLockOutOfReachMessage,
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is CounldntCloseMovingException)
{
@ -374,7 +472,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
AppResources.ErrorCloseLockMovingMessage,
"OK");
AppResources.MessageAnswerOk);
}
else if (exception is CouldntCloseBoldBlockedException)
{
@ -383,7 +481,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
AppResources.ErrorCloseLockBoldBlockedMessage,
"OK");
AppResources.MessageAnswerOk);
}
else
{
@ -391,34 +489,47 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
exception.Message,
"OK");
AppResources.MessageAnswerOk);
}
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
? stateAwareException.State
: LockingState.Disconnected;
// Wait until cancel getting geolocation has completed.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
try
{
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
}
// Get geoposition.
var timeStamp = DateTime.Now;
BikesViewModel.ActionText = "Abfrage Standort...";
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
Location currentLocation = null;
try
{
currentLocation = await Geolocation.GetAsync(timeStamp);
await Task.WhenAny(new List<Task> { currentLocationTask });
currentLocation = currentLocationTask.Result;
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
Log.ForContext<BookedOpen>().Information("Getting geolocation when closing lock of bike {Bike} failed. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = "Keine Standortinformationen verfügbar.";
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
}
// Lock list to avoid multiple taps while copri action is pending.

View file

@ -15,6 +15,8 @@ using TINK.Model.User;
using Xamarin.Essentials;
using TINK.Repository.Request;
using TINK.Model.Device;
using System.Threading;
using System.Collections.Generic;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
@ -100,7 +102,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntOpenBoldWasBlockedException)
{
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockStillOpenTitle,
@ -206,14 +208,32 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
// Unlock bike.
BikesViewModel.IsIdle = false;
Log.ForContext<BookedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
// Start getting geolocation.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
var ctsLocation = new CancellationTokenSource();
Task<Location> currentLocationTask = null;
var timeStamp = DateTime.Now;
try
{
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
}
// Stop polling before returning bike.
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
// Close lock
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
try
{
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.Disconnected;
@ -222,6 +242,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
BikesViewModel.ActionText = string.Empty;
// Signal cts to cancel getting geolocation.
ctsLocation.Cancel();
if (exception is OutOfReachException)
{
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
@ -261,27 +284,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
? stateAwareException.State
: LockingState.Disconnected;
// Wait until cancel getting geolocation has completed.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
try
{
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedUnknown>().Information("Canceling query location failed on unexpected lock state failed. {Exception}", SelectedBike, ex);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
}
// Get geoposition.
var timeStamp = DateTime.Now;
BikesViewModel.ActionText = "Abfrage Standort...";
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
Location currentLocation = null;
try
{
currentLocation = await Geolocation.GetAsync(timeStamp);
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
currentLocation = currentLocationTask?.Result ?? null;
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<BookedUnknown>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
Log.ForContext<BookedUnknown>().Information("Get geolocation failed when closing lock of bike {Bike} with unknown state. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = "Keine Standortinformationen verfügbar.";
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
}
// Lock list to avoid multiple taps while copri action is pending.

View file

@ -187,7 +187,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Ask whether to really book bike?
alertResult = await ViewService.DisplayAlert(
string.Empty,
string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()),
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -287,7 +287,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntOpenBoldWasBlockedException)
{
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
Log.ForContext<DisposableDisconnected>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockStillOpenTitle,

View file

@ -94,8 +94,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
// Close lock
Log.ForContext<DisposableOpen>().Information("User selected disposable bike {bike} in order to close lock.", SelectedBike);
// Unlock bike.
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
try
{
@ -115,7 +113,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CounldntCloseMovingException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
@ -124,7 +122,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntCloseBoldBlockedException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
Log.ForContext<DisposableOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
@ -160,7 +158,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
catch (Exception exception)
{
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
}
@ -249,7 +247,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
catch (Exception exception)
{
Log.ForContext<ReservedClosed>().Error("Lock can not be disconnected. {Exception}", exception);
Log.ForContext<DisposableOpen>().Error("Lock can not be disconnected. {Exception}", exception);
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
}

View file

@ -65,7 +65,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
try
{
// Switch to map page
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
ViewService.ShowPage(ViewTypes.LoginPage);
#else
await ViewService.ShowPage("//LoginPage");

View file

@ -167,7 +167,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Ask whether to really book bike?
var l_oResult = await ViewService.DisplayAlert(
string.Empty,
string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()),
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -244,7 +244,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntOpenBoldIsBlockedException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockTitle,
@ -253,7 +253,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntOpenBoldWasBlockedException)
{
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockStillOpenTitle,
@ -263,7 +263,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
&& inconsistentState.State == LockingState.Closed)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
Log.ForContext<ReservedClosed>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockTitle,
@ -337,20 +337,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
}
else if (exception is ResponseException copriException)
{
// Copri server is not reachable.
Log.ForContext<BookedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
Log.ForContext<ReservedClosed>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
}
else
{
Log.ForContext<BookedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
Log.ForContext<ReservedClosed>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
}
}

View file

@ -270,7 +270,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Ask whether to really book bike?
var alertResult = await ViewService.DisplayAlert(
string.Empty,
string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()),
string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()),
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
@ -370,7 +370,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntOpenBoldWasBlockedException)
{
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
Log.ForContext<ReservedDisconnected>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockStillOpenTitle,

View file

@ -171,7 +171,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Close lock and cancel reservation.
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
try
{
@ -182,7 +181,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
BikesViewModel.ActionText = string.Empty;
if (exception is OutOfReachException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
@ -191,7 +190,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CounldntCloseMovingException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock bike is moving. {Exception}", exception);
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock bike is moving. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
@ -200,7 +199,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntCloseBoldBlockedException)
{
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
Log.ForContext<ReservedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,
@ -209,7 +208,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else
{
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
Log.ForContext<ReservedOpen>().Error("Lock can not be closed. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorCloseLockTitle,

View file

@ -15,6 +15,8 @@ using TINK.Model.User;
using Xamarin.Essentials;
using TINK.Repository.Request;
using TINK.Model.Device;
using System.Threading;
using System.Collections.Generic;
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
@ -54,7 +56,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
public override InUseStateEnum State => InUseStateEnum.Reserved;
/// <summary> Open bike and update COPRI lock state. </summary>
public async Task<IRequestHandler> HandleRequestOption1()
public async Task<IRequestHandler> HandleRequestOption1() => await OpenLock();
/// <summary> Open bike and update COPRI lock state. </summary>
public async Task<IRequestHandler> OpenLock()
{
// Unlock bike.
Log.ForContext<ReservedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
@ -93,7 +98,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
else if (exception is CouldntOpenBoldWasBlockedException)
{
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorOpenLockStillOpenTitle,
@ -195,18 +200,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
}
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
public async Task<IRequestHandler> HandleRequestOption2()
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
public async Task<IRequestHandler> CloseLock()
{
// Unlock bike.
BikesViewModel.IsIdle = false;
Log.ForContext<ReservedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
// Start getting geolocation.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
var ctsLocation = new CancellationTokenSource();
Task<Location> currentLocationTask = null;
var timeStamp = DateTime.Now;
try
{
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
}
// Stop polling before returning bike.
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
await ViewUpdateManager().StopUpdatePeridically();
// Close lock
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
try
{
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.Disconnected;
@ -215,6 +242,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
BikesViewModel.ActionText = string.Empty;
// Signal cts to cancel getting geolocation.
ctsLocation.Cancel();
if (exception is OutOfReachException)
{
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
@ -254,27 +284,40 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
? stateAwareException.State
: LockingState.Disconnected;
// Wait until cancel getting geolocation has completed.
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
try
{
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<ReservedUnknown>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
}
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
await ViewUpdateManager().StartUpdateAyncPeridically();
BikesViewModel.ActionText = string.Empty;
BikesViewModel.IsIdle = true;
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
}
// Get geoposition.
var timeStamp = DateTime.Now;
BikesViewModel.ActionText = "Abfrage Standort...";
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
Location currentLocation = null;
try
{
currentLocation = await Geolocation.GetAsync(timeStamp);
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
currentLocation = currentLocationTask?.Result ?? null;
}
catch (Exception ex)
{
// No location information available.
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
BikesViewModel.ActionText = "Keine Standortinformationen verfügbar.";
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
}
// Lock list to avoid multiple taps while copri action is pending.

View file

@ -145,7 +145,7 @@ namespace TINK.ViewModel.BikesAtStation
/// <summary> Command object to bind login page redirect link to view model.</summary>
public System.Windows.Input.ICommand ContactSupportClickedCommand
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
=> new Xamarin.Forms.Command(() => OpenSupportPageAsync());
#else
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
@ -153,14 +153,14 @@ namespace TINK.ViewModel.BikesAtStation
/// <summary> Command object to bind login page redirect link to view model.</summary>
public System.Windows.Input.ICommand LoginRequiredHintClickedCommand
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
=> new Xamarin.Forms.Command(() => OpenLoginPageAsync());
#else
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
#endif
/// <summary> Opens login page. </summary>
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void OpenLoginPageAsync()
#else
public async Task OpenLoginPageAsync()
@ -170,7 +170,7 @@ namespace TINK.ViewModel.BikesAtStation
{
// Switch to map page
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
ViewService.ShowPage(ViewTypes.LoginPage);
#else
await ViewService.ShowPage("//LoginPage");
@ -184,7 +184,7 @@ namespace TINK.ViewModel.BikesAtStation
}
/// <summary> Opens support. </summary>
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void OpenSupportPageAsync()
#else
public async Task OpenSupportPageAsync()
@ -194,7 +194,7 @@ namespace TINK.ViewModel.BikesAtStation
{
// Switch to map page
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingFeedbackAndContact);
#else
await ViewService.ShowPage("//LoginPage");

View file

@ -166,14 +166,14 @@ namespace TINK.ViewModel.Info
/// <summary> Command object to bind login button to view model. </summary>
public ICommand OnSelectStationRequest
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
=> new Xamarin.Forms.Command(() => OpenSelectStationPage());
#else
=> new Xamarin.Forms.Command(async () => await OpenSelectStationPageAsync());
#endif
/// <summary> Opens login page. </summary>
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void OpenSelectStationPage()
#else
public async Task OpenSelectStationPageAsync()
@ -183,7 +183,7 @@ namespace TINK.ViewModel.Info
{
// Switch to map page
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
ViewService.PushAsync(ViewTypes.SelectStationPage);
#else
await ViewService.ShowPage("//SelectStationPage");

View file

@ -12,7 +12,7 @@ using System.Threading.Tasks;
using System.ComponentModel;
using Xamarin.Forms.GoogleMaps;
using System.Collections.ObjectModel;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.Settings;
@ -66,7 +66,7 @@ namespace TINK.ViewModel.Contact
/// <summary>Delegate to perform navigation.</summary>
private INavigation m_oNavigation;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigationMasterDetail;
#endif
@ -142,14 +142,14 @@ namespace TINK.ViewModel.Contact
m_oNavigation = navigation
?? throw new ArgumentException("Can not instantiate map page view model- object. No navigation service available.");
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
#endif
IsConnected = TinkApp.GetIsConnected();
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary> Delegate to perform navigation.</summary>
public INavigationMasterDetail NavigationMasterDetail
{
@ -285,7 +285,7 @@ namespace TINK.ViewModel.Contact
if (Pins.Count <= 0)
{
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
ActionText = AppResources.ActivityTextRequestingLocationPermissions;
// Check location permission
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();

View file

@ -99,7 +99,7 @@ namespace TINK.ViewModel.FindBike
{
Log.ForContext<FindBikePageViewModel>().Information("User request to show page FindBike- page re-appearing");
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
ActionText = AppResources.ActivityTextFindBikeLoadingBikes;
var bikes = await ConnectorFactory(IsConnected).Query.GetBikesAsync();

View file

@ -142,7 +142,7 @@ namespace TINK.ViewModel.Info.BikeInfo
public IList<CourouselPageItemViewModel> CarouselItems { get; }
/// <summary> Command object to bind close button to view model. </summary>
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
private Action CloseAction
=> () => m_oViewService.ShowPage(ViewTypes.MapPage);
#else

View file

@ -317,7 +317,7 @@ namespace TINK.ViewModel
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.FILTERTINKGENERAL))
{
// No need to show "Anleitung TINK Räder" because user can not use tink.
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
m_oViewService.ShowPage(ViewTypes.MapPage);
#else
await m_oViewService.ShowPage("//MapPage");
@ -326,7 +326,7 @@ namespace TINK.ViewModel
}
// Swich to map page
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions);
#else
await m_oViewService.ShowPage("//MapPage");

View file

@ -12,7 +12,7 @@ using System.Threading.Tasks;
using System.ComponentModel;
using Xamarin.Forms.GoogleMaps;
using System.Collections.ObjectModel;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.Settings;
@ -74,7 +74,7 @@ namespace TINK.ViewModel.Map
/// <summary>Delegate to perform navigation.</summary>
private INavigation m_oNavigation;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigationMasterDetail;
#endif
@ -151,7 +151,7 @@ namespace TINK.ViewModel.Map
m_oViewUpdateManager = new IdlePollingUpdateTaskManager();
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
#endif
@ -176,7 +176,7 @@ namespace TINK.ViewModel.Map
}
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary> Delegate to perform navigation.</summary>
public INavigationMasterDetail NavigationMasterDetail
{
@ -315,7 +315,7 @@ namespace TINK.ViewModel.Map
// Update map page filter
ActiveFilterMap = TinkApp.GroupFilterMapPage;
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
ActionText = AppResources.ActivityTextRequestingLocationPermissions;
// Check location permission
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
@ -325,13 +325,13 @@ namespace TINK.ViewModel.Map
{
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageCenterMapLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
AppResources.MessageCenterMapLocationPermissionOpenDialog,
AppResources.MessageAnswerYes,
AppResources.MessageAnswerNo);
if (dialogResult)
{

View file

@ -13,6 +13,7 @@ using TINK.Model.Bikes.Bike.BC;
using TINK.Repository;
using System.Net;
using TINK.MultilingualResources;
using System.Linq;
namespace TINK.ViewModel
{
@ -59,9 +60,9 @@ namespace TINK.ViewModel
/// </summary>
/// <param name="p_oStation">Station to get id from</param>
/// <returns></returns>
public static int GetStationId(string p_strStationName)
public static int GetStationId(string stationName)
{
return int.Parse(p_strStationName.Replace(USER_FIENDLY_STATIONNUMBER_PREFIX, "").Trim());
return int.Parse(stationName.Replace(USER_FIENDLY_STATIONNUMBER_PREFIX, "").Trim());
}
/// <summary> Get full display name of a bike which includes id. </summary>
@ -91,11 +92,11 @@ namespace TINK.ViewModel
/// <summary>
/// Maps state to color.
/// </summary>
/// <param name="p_eState"></param>
/// <param name="state"></param>
/// <returns></returns>
public static Color GetColor(this InUseStateEnum p_eState)
public static Color GetColor(this InUseStateEnum state)
{
switch (p_eState)
switch (state)
{
case InUseStateEnum.Disposable:
return Color.Default;
@ -158,33 +159,48 @@ namespace TINK.ViewModel
}
/// <summary> Gets message that logged in user has not booked any bikes. </summary>
public static FormattedString GetErrorInfoText(this Exception p_oException)
/// <summary> Gets error message and handles aggegate exceptions. </summary>
public static string GetErrorMessage(this Exception exception)
{
if (p_oException == null)
if (exception == null)
return string.Empty;
if (!(exception is AggregateException aggregateException))
return exception.Message;
if (aggregateException.InnerExceptions.Count == 1)
return aggregateException.InnerExceptions[0].Message;
return new AggregateException().Message + "\r\n"+ string.Join("\r\n", aggregateException.InnerExceptions.Select(x => x.Message));
}
/// <summary> Gets message that logged in user has not booked any bikes. </summary>
public static FormattedString GetErrorInfoText(this Exception exception)
{
if (exception == null)
{
return string.Empty;
}
FormattedString l_oError;
// An error occurred getting bikes information.
if (p_oException is WebConnectFailureException)
if (exception is WebConnectFailureException)
{
l_oError = new FormattedString();
l_oError.Spans.Add(new Span { Text = "Information!\r\n", FontAttributes = FontAttributes.Bold });
l_oError.Spans.Add(new Span { Text = $"{p_oException.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" });
l_oError.Spans.Add(new Span { Text = $"{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" });
return l_oError;
}
else if (p_oException is InvalidResponseException)
else if (exception is InvalidResponseException)
{
l_oError = new FormattedString();
l_oError.Spans.Add(new Span { Text = "Fehler, ungültige Serverantwort!\r\n", FontAttributes = FontAttributes.Bold });
l_oError.Spans.Add(new Span { Text = $"{p_oException.Message}" });
l_oError.Spans.Add(new Span { Text = $"{exception.Message}" });
return l_oError;
}
else if (p_oException is WebForbiddenException)
else if (exception is WebForbiddenException)
{
l_oError = new FormattedString();
l_oError.Spans.Add(new Span { Text = "Beschäftigt... Einen Moment bitte!" });
@ -193,37 +209,37 @@ namespace TINK.ViewModel
l_oError = new FormattedString();
l_oError.Spans.Add(new Span { Text = "Allgemeiner Fehler!\r\n", FontAttributes = FontAttributes.Bold });
l_oError.Spans.Add(new Span { Text = $"{p_oException}" });
l_oError.Spans.Add(new Span { Text = $"{exception}" });
return l_oError;
}
/// <summary> User tabbed a URI. </summary>
/// <param name="p_oSender">Sender of the event.</param>
/// <param name="p_eEventArgs">Event arguments</param>
public static void OnNavigating(object p_oSender, WebNavigatingEventArgs p_eEventArgs)
/// <param name="sender">Sender of the event.</param>
/// <param name="eventArgs">Event arguments</param>
public static void OnNavigating(object sender, WebNavigatingEventArgs eventArgs)
{
if (!p_eEventArgs.Url.ToUpper().StartsWith("HTTP"))
if (!eventArgs.Url.ToUpper().StartsWith("HTTP"))
{
// An internal link was detected.
// Stay inside WebView
p_eEventArgs.Cancel = false;
eventArgs.Cancel = false;
return;
}
// Do not navigate outside the document.
p_eEventArgs.Cancel = true;
eventArgs.Cancel = true;
DependencyService.Get<IExternalBrowserService>().OpenUrl(p_eEventArgs.Url);
DependencyService.Get<IExternalBrowserService>().OpenUrl(eventArgs.Url);
}
/// <summary> Gets the user group if a user friendly name.</summary>
/// <param name="p_oUser"></param>
/// <param name="user"></param>
/// <returns></returns>
public static string GetUserGroupDisplayName(this User p_oUser)
public static string GetUserGroupDisplayName(this User user)
{
return string.Join(" & ", p_oUser.Group);
return string.Join(" & ", user.Group);
}