Version 3.0.339

This commit is contained in:
Anja 2022-09-16 11:19:46 +02:00
parent 0468955d49
commit 52c9f6f1d9
43 changed files with 993 additions and 614 deletions

View file

@ -0,0 +1,9 @@
using Xamarin.Essentials;
namespace TINK.Model
{
public static class CurrentAppInfos
{
public static string CurrentAppVersion => VersionTracking.CurrentVersion;
}
}

View file

@ -1,4 +1,4 @@
using Xamarin.Essentials;
using Xamarin.Essentials;
namespace TINK.Model.Device
{
@ -14,6 +14,7 @@ namespace TINK.Model.Device
/// <summary> Device Model (SMG-950U, iPhone10,6). </summary>
string Model { get; }
/// <summary> Operation system. </summary>
DevicePlatform Platform { get; }
/// <summary> Operating System Version Number (7.0) as text</summary>

View file

@ -0,0 +1,16 @@
using System;
using Serilog;
using TINK.Model.Device;
namespace TINK.Model.Logging
{
public class AppAndEnvironmentInfo
{
public void LogHeader(ISmartDevice device, AppFlavor appFlavor, Version appVersion)
{
Log.ForContext<AppAndEnvironmentInfo>().Information($"App: {appFlavor.GetDisplayName()}, version {appVersion}");
Log.ForContext<AppAndEnvironmentInfo>().Information($"OS: {device.Platform}, version: {device.VersionText}");
Log.ForContext<AppAndEnvironmentInfo>().Information($"Device: {device.Model}, manufacturer: {device.Manufacturer}");
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;
@ -190,6 +190,9 @@ namespace TINK.Model
Flavor = flavor;
// Log application and environment information.
new AppAndEnvironmentInfo().LogHeader(device, flavor, currentVersion);
var locksServices = locksService != null
? new HashSet<ILocksService> { locksService }
: new HashSet<ILocksService> {
@ -440,15 +443,50 @@ namespace TINK.Model
LoggingLevelSwitch levelSwitch,
string logFilePath)
{
bool LogToFileFilter(LogEvent e)
{
if (e.Level >= levelSwitch.MinimumLevel)
{
// If level is above global logging level do log.
return true;
}
if (!e.Properties.ContainsKey(Constants.SourceContextPropertyName))
{
// Do not log if source context is not available.
return false;
}
var sourceContex = e.Properties[Constants.SourceContextPropertyName].ToString();
if ((e.Level == LogEventLevel.Information) &&
(sourceContex.Contains(typeof(AppAndEnvironmentInfo).Namespace) /* Log App and enviroment info. */
|| sourceContex.Contains(typeof(ViewModel.Bikes.Bike.BluetoothLock.RequestHandler.Base).Namespace /* Log info-level messages to provide context for bluetooth log. */ )))
{
return true;
}
if (e.Level >= LogEventLevel.Debug
&& sourceContex.Contains(typeof(LockItBase).Namespace /*Scanning, connect and management functionality */))
{
return true;
}
return false;
}
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitch)
.MinimumLevel.Override("TINK.Services.BluetoothLock.BLE", LogEventLevel.Debug) /* Scanning, connect and management functionality */
.MinimumLevel.Override("TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler", LogEventLevel.Information) /* Provides use case context */
.WriteTo.Debug()
.WriteTo.File(logFilePath, Logging.RollingInterval.Session)
.WriteTo.Logger(lg => lg
.MinimumLevel.ControlledBy(new LoggingLevelSwitch(LogEventLevel.Debug))
.Filter.ByIncludingOnly(Matching.FromSource("TINK.Services.BluetoothLock.BLE"))
.MinimumLevel.Verbose()
.WriteTo.Logger(consoleLoggerConfig => consoleLoggerConfig
.MinimumLevel.Information()
.WriteTo.Debug()
)
.WriteTo.Logger(fileLoggerConfig => fileLoggerConfig
.Filter.ByIncludingOnly(e => LogToFileFilter(e))
.WriteTo.File(logFilePath, Logging.RollingInterval.Session)
)
.WriteTo.Logger(copriLoggerConfig => copriLoggerConfig
.MinimumLevel.Debug()
.Filter.ByIncludingOnly(Matching.FromSource(typeof(LockItBase/*Scanning, connect and management functionality */).Namespace))
.WriteTo.MemoryQueueSink()
)
.CreateLogger();

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using TINK.MultilingualResources;
using Xamarin.Essentials;
@ -584,6 +584,16 @@ namespace TINK.Model
new Version(3, 0, 338),
AppResources.ChangeLog3_0_338_SB,
new List<AppFlavor> { AppFlavor.ShareeBike }
},
{
new Version(3, 0, 339),
AppResources.ChangeLog3_0_339_SB_LB,
new List<AppFlavor> { AppFlavor.LastenradBayern, AppFlavor.ShareeBike }
},
{
new Version(3, 0, 339),
AppResources.ChangeLog3_0_339_MK,
new List<AppFlavor> { AppFlavor.MeinKonrad }
}
};

View file

@ -1210,6 +1210,26 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to The cargo bikes from the suburbs now show their home station in their name. These bikes must be returned there!
///
///You can now see at a glance which app version you have installed: in the menu at the very bottom. Please update the app regularly to be up to date in functionality and design!.
/// </summary>
public static string ChangeLog3_0_339_MK {
get {
return ResourceManager.GetString("ChangeLog3_0_339_MK", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You can now see at a glance which app version you have installed: in the menu at the very bottom. Please update the app regularly to be up to date in functionality and design!.
/// </summary>
public static string ChangeLog3_0_339_SB_LB {
get {
return ResourceManager.GetString("ChangeLog3_0_339_SB_LB", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to query available bikes..
/// </summary>
@ -2636,6 +2656,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Enter bike number here.
/// </summary>
public static string PlaceholderFindBike {
get {
return ResourceManager.GetString("PlaceholderFindBike", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No.
/// </summary>

View file

@ -1020,4 +1020,15 @@ Außerdem: Kleine Grafiken lassen auf einen Blick erkennen um was für einen Rad
<data name="QuestionSupportmailTitle" xml:space="preserve">
<value>Einwilligung</value>
</data>
<data name="PlaceholderFindBike" xml:space="preserve">
<value>Fahrrad-Nummer hier eingeben</value>
</data>
<data name="ChangeLog3_0_339_MK" xml:space="preserve">
<value>Die Lastenräder aus den Vororten zeigen nun ihre Heimatstation im Namen an. Diese Räder müssen dort wieder abgeben werden!
Sie können nun auf einen Blick sehen, welche App-Version Sie installiert haben: im Menü ganz unten. Bitte aktualisieren Sie die App regelmäßig, um in Funktionalität und Design auf dem neuesten Stand zu sein!</value>
</data>
<data name="ChangeLog3_0_339_SB_LB" xml:space="preserve">
<value>Sie können nun auf einen Blick sehen, welche App-Version Sie installiert haben: im Menü ganz unten. Bitte aktualisieren Sie die App regelmäßig, um in Funktionalität und Design auf dem neuesten Stand zu sein!</value>
</data>
</root>

View file

@ -1112,4 +1112,15 @@ In addition: Small graphics let you see at a glance what type of bike it is.</va
<data name="QuestionSupportmailTitle" xml:space="preserve">
<value>Consent</value>
</data>
<data name="PlaceholderFindBike" xml:space="preserve">
<value>Enter bike number here</value>
</data>
<data name="ChangeLog3_0_339_MK" xml:space="preserve">
<value>The cargo bikes from the suburbs now show their home station in their name. These bikes must be returned there!
You can now see at a glance which app version you have installed: in the menu at the very bottom. Please update the app regularly to be up to date in functionality and design!</value>
</data>
<data name="ChangeLog3_0_339_SB_LB" xml:space="preserve">
<value>You can now see at a glance which app version you have installed: in the menu at the very bottom. Please update the app regularly to be up to date in functionality and design!</value>
</data>
</root>

View file

@ -1384,6 +1384,22 @@ Außerdem: Kleine Grafiken lassen auf einen Blick erkennen um was für einen Rad
<source>Consent</source>
<target state="translated">Einwilligung</target>
</trans-unit>
<trans-unit id="PlaceholderFindBike" translate="yes" xml:space="preserve">
<source>Enter bike number here</source>
<target state="translated">Fahrrad-Nummer hier eingeben</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_339_MK" translate="yes" xml:space="preserve">
<source>The cargo bikes from the suburbs now show their home station in their name. These bikes must be returned there!
You can now see at a glance which app version you have installed: in the menu at the very bottom. Please update the app regularly to be up to date in functionality and design!</source>
<target state="translated">Die Lastenräder aus den Vororten zeigen nun ihre Heimatstation im Namen an. Diese Räder müssen dort wieder abgeben werden!
Sie können nun auf einen Blick sehen, welche App-Version Sie installiert haben: im Menü ganz unten. Bitte aktualisieren Sie die App regelmäßig, um in Funktionalität und Design auf dem neuesten Stand zu sein!</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_339_SB_LB" translate="yes" xml:space="preserve">
<source>You can now see at a glance which app version you have installed: in the menu at the very bottom. Please update the app regularly to be up to date in functionality and design!</source>
<target state="translated">Sie können nun auf einen Blick sehen, welche App-Version Sie installiert haben: im Menü ganz unten. Bitte aktualisieren Sie die App regelmäßig, um in Funktionalität und Design auf dem neuesten Stand zu sein!</target>
</trans-unit>
</group>
</body>
</file>

View file

@ -1,11 +1,13 @@
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
namespace TINK.Repository.Exception
{
public class NotAtStationException : InvalidResponseException
{
/// <summary> COPRI response status regular expression. </summary>
public const string RETURNBIKE_FAILURE_STATUS_MESSAGE_UPPERCASE = "(FAILURE 2178: BIKE [0-9]+ OUT OF GEO FENCING\\. )([0-9]+)( METER DISTANCE TO NEXT STATION )([0-9]+)";
public const string RETURNBIKE_FAILURE_STATUS_MESSAGE_CODE = "FAILURE 2178:";
/// <summary> COPRI response status regular expression to extract detail information. </summary>
public const string RETURNBIKE_FAILURE_STATUS_MESSAGE_UPPERCASE = "(BIKE [A-Za-z0-9_]+ OUT OF GEO FENCING\\. )([0-9]+)( METER DISTANCE TO NEXT STATION )([A-Za-z0-9_]+)";
/// <summary> Prevents invalid use of exception. </summary>
private NotAtStationException() : base(typeof(NotAtStationException).Name)
@ -15,25 +17,36 @@ namespace TINK.Repository.Exception
public static bool IsNotAtStation(string responseState, out NotAtStationException exception)
{
// Check if there are too many bikes requested/ booked.
var match = Regex.Match(
responseState.ToUpper(),
RETURNBIKE_FAILURE_STATUS_MESSAGE_UPPERCASE);
if (match.Groups.Count != 5
|| !int.TryParse(match.Groups[2].ToString(), out int meters)
|| !int.TryParse(match.Groups[4].ToString(), out int stationNr))
var response = responseState.Trim().ToUpper();
if (!response.StartsWith(RETURNBIKE_FAILURE_STATUS_MESSAGE_CODE))
{
exception = null;
return false;
}
exception = new NotAtStationException { Distance = meters, StationNr = stationNr };
var match = Regex.Match(
responseState.ToUpper(),
RETURNBIKE_FAILURE_STATUS_MESSAGE_UPPERCASE);
if (match.Groups.Count != 5
|| !int.TryParse(match.Groups[2].ToString(), out int meters))
{
exception = new NotAtStationException();
return true;
}
exception = new NotAtStationException {
Distance = meters,
StationNr = match.Groups[4].ToString()
};
return true;
}
/// <summary> Holds the maximum count of bikes allowed to reserve/ book.</summary>
public int Distance { get; private set; }
public int? Distance { get; private set; } = null;
/// <summary> Holds the maximum count of bikes allowed to reserve/ book.</summary>
public int StationNr { get; private set; }
public string StationNr { get; private set; } = string.Empty;
}
}

View file

@ -63,6 +63,14 @@
<ProjectReference Include="..\LockItBLE\LockItBLE.csproj" />
<ProjectReference Include="..\LockItShared\LockItShared.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Mono.Android">
<HintPath>..\..\..\..\..\..\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\MonoAndroid\v11.0\Mono.Android.dll</HintPath>
</Reference>
<Reference Include="Xamarin.iOS">
<HintPath>..\..\..\..\..\..\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\Xamarin.iOS\v1.0\Xamarin.iOS.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Update="MultilingualResources\AppResources.Designer.cs">
<DesignTime>True</DesignTime>

View file

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.Themes.ShareeBike">
<Color x:Key="primary-back-title-color">#009899</Color>
<!--Main color-->
<Color x:Key="primary-back-title-color">#009899</Color>
<!--Primary Button-->
<Style TargetType="Button">
<Setter Property="WidthRequest" Value="400" />
<Setter Property="HorizontalOptions" Value="Center" />
@ -27,6 +29,8 @@
</Trigger>
</Style.Triggers>
</Style>
<!--Secondary Button-->
<Style x:Key="SecondaryButton" TargetType="Button">
<Setter Property="WidthRequest" Value="400" />
<Setter Property="HorizontalOptions" Value="Center" />
@ -50,6 +54,7 @@
</Style.Triggers>
</Style>
<!--Switch-->
<Style TargetType="Switch">
<Style.Triggers>
<Trigger TargetType="Switch"
@ -64,17 +69,24 @@
</Trigger>
</Style.Triggers>
</Style>
<!--Slider-->
<Style TargetType="Slider">
<Setter Property="ThumbColor" Value="{DynamicResource Key=primary-back-title-color}"/>
<Setter Property="Background" Value="LightGray"/>
</Style>
<!--Label-->
<Style TargetType="Label">
<Setter Property="FontSize" Value="Default"/>
</Style>
<!--Flyout Item-->
<Style TargetType="FlyoutItem">
<Setter Property="Shell.BackgroundColor" Value="{DynamicResource Key=primary-back-title-color}" />
</Style>
<!--Navbar-->
<Style x:Key="Label-Navbar" TargetType="Label">
<Setter Property="FontSize" Value="20"/>
<!--<Setter Property="TextTransform" Value="Uppercase"/>-->
@ -83,10 +95,10 @@
<Setter Property="HorizontalOptions" Value="Start"/>
<Setter Property="Grid.Column" Value="1"/>
</Style>
<Style x:Key="Image-Navbar" TargetType="Image">
<Setter Property="Source" Value="swk_theme.png"/>
<Setter Property="Aspect" Value="AspectFill"/>
<Setter Property="Grid.ColumnSpan" Value="2"/>
</Style>
</ResourceDictionary>
<Style x:Key="Image-Navbar" TargetType="Image">
<Setter Property="Source" Value="swk_theme.png"/>
<Setter Property="Aspect" Value="AspectFill"/>
<Setter Property="Grid.ColumnSpan" Value="2"/>
</Style>
</ResourceDictionary>

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -174,7 +174,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed (Copri server not reachable).", SelectedBike);
await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle,
@ -185,7 +185,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is NotAtStationException notAtStationException)
{
// COPRI returned an error.
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an not at station error.", SelectedBike);
Log.ForContext<BookedClosed>().Information(
"User selected booked bike {bike} but returning failed. COPRI returned out of GEO fencing error. Postion send to COPRI {@position}.",
SelectedBike,
currentLocationDto);
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeTitle,
@ -195,7 +198,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is NoGPSDataException)
{
// COPRI returned an error.
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error.", SelectedBike);
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an no GPS- data error.", SelectedBike);
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeTitle,
@ -205,7 +208,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is ResponseException copriException)
{
// COPRI returned an error.
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an error.", SelectedBike);
await ViewService.DisplayAdvancedAlert(
"Statusfehler beim Zurückgeben des Rads!",

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -145,7 +145,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returning failed (Copri server not reachable).", SelectedBike);
await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle,
@ -343,20 +343,23 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
IsConnected = IsConnectedDelegate();
var feedBackUri = SelectedBike?.OperatorUri;
LocationDto currentLocationDto = null;
BookingFinishedModel bookingFinished;
try
{
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
SelectedBike,
currentLocation != null
? new LocationDto.Builder
currentLocationDto = currentLocation != null
? new LocationDto.Builder
{
Latitude = currentLocation.Latitude,
Longitude = currentLocation.Longitude,
Accuracy = currentLocation.Accuracy ?? double.NaN,
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
}.Build()
: null,
: null;
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
SelectedBike,
currentLocationDto,
SmartDevice);
// If canceling bike succedes remove bike because it is not ready to be booked again
IsRemoveBikeRequired = true;
@ -367,7 +370,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
Log.ForContext<BookedOpen>().Information(
"User selected booked bike {bike} but returning failed (Copri server not reachable).", SelectedBike);
await ViewService.DisplayAdvancedAlert(
AppResources.ErrorReturnBikeNoWebTitle,
@ -378,7 +382,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is NotAtStationException notAtStationException)
{
// COPRI returned an error.
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
Log.ForContext<BookedOpen>().Information(
"User selected booked bike {bike} but returning failed. COPRI returned out of GEO fencing error. Postion send to COPRI {@position}.",
SelectedBike,
currentLocationDto);
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeTitle,
@ -388,7 +395,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is NoGPSDataException)
{
// COPRI returned an error.
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error.", SelectedBike);
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returning failed. COPRI returned an no GPS- data error.", SelectedBike);
await ViewService.DisplayAlert(
AppResources.ErrorReturnBikeTitle,
@ -398,7 +405,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
else if (exception is ResponseException copriException)
{
// Copri server is not reachable.
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returning failed. COPRI returned an error.", SelectedBike);
await ViewService.DisplayAdvancedAlert(
"Statusfehler beim Zurückgeben des Rads!",