Code updated to 3.0.238

This commit is contained in:
Oliver Hauff 2021-06-26 20:57:55 +02:00
parent 3302d80678
commit 9c6a1fa92b
257 changed files with 7763 additions and 2861 deletions

View file

@ -6,8 +6,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Plugin.BLE" Version="2.1.1" />
<PackageReference Include="Polly" Version="7.2.1" />
<PackageReference Include="Plugin.BLE" Version="2.1.2" />
<PackageReference Include="Polly" Version="7.2.2" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
</ItemGroup>

View file

@ -141,13 +141,17 @@ namespace TINK.Services.BluetoothLock.BLE
switch (lockingState.Value)
{
case LockitLockingState.CouldntOpenBoldBlocked:
// Expected error. ILockIt count not be opened (Spoke blocks lock, ....)
throw new CouldntOpenBoldBlockedException();
case LockitLockingState.Open:
return lockingState.Value;
case LockitLockingState.CouldntOpenBoldBlocked:
// Expected error. ILockIt count not be opened (Spoke blocks lock, ....)
throw new CouldntOpenBoldIsBlockedException();
case LockitLockingState.Unknown:
// Expected error. ILockIt count not be opened (Spoke has blocked/ blocks lock, ....)
throw new CouldntOpenBoldWasBlockedException();
default:
// Comprises values
// - LockitLockingState.Closed

View file

@ -109,18 +109,22 @@ namespace TINK.Services.BluetoothLock.BLE
switch (info.State.Value)
{
case LockitLockingState.CouldntOpenBoldBlocked:
// Expected error. ILockIt count not be opened (Spoke blocks lock, ....)
throw new CouldntOpenBoldBlockedException();
case LockitLockingState.Open:
return info.State.Value;
case LockitLockingState.CouldntOpenBoldBlocked:
// Expected error. ILockIt count not be opened (Spoke blocks lock, ....)
throw new CouldntOpenBoldIsBlockedException();
case LockitLockingState.Unknown:
// Expected error. ILockIt count not be opened (Spoke has blocked/ blocks lock, ....)
throw new CouldntOpenBoldWasBlockedException();
default:
// Comprises values
// - LockitLockingState.Closed
// - LockitLockingState.Unknown
// - LockitLockingState.CouldntOpenBoldBlocked
// - LockitLockingState.CouldntCloseMoving (should never happen because command open was send)
// - LockitLockingState.CouldntCloseBoldBlocked (should never happen because command open was send)
// Internal error which sould never occure. Lock refuses to open but connection is ok.
throw new CouldntOpenInconsistentStateExecption(info.State.Value.GetLockingState());
}

View file

@ -23,7 +23,7 @@
<Folder Include="Services\BluetoothLock\Tdo\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" />
</ItemGroup>
<ItemGroup>

View file

@ -18,11 +18,6 @@
<source>Unexpected locking state "{0}" detected after sending open command.</source>
<target state="translated">Unerwarteter Schlosszustand "{0}" gemeldet nach Ausführen des Öffnen-Befehls.</target>
</trans-unit>
<trans-unit id="ErrorOpenLockUnknownPosition" translate="yes" xml:space="preserve">
<source>Lock reports unknown bold position.</source>
<target state="needs-review-translation">Schloss meldet unbekannten Schließzustand.</target>
<note from="MultilingualUpdate" annotates="source" priority="2">Please verify the translations accuracy as the source string was updated after it was translated.</note>
</trans-unit>
<trans-unit id="ErrorBluetoothDisconnectedException" translate="yes" xml:space="preserve">
<source>No bluetooth connection.</source>
<target state="translated">Keine Bluetooth-Verbindung.</target>
@ -39,6 +34,10 @@
<source>Bold is blocked.</source>
<target state="translated">Schloss ist blockiert.</target>
</trans-unit>
<trans-unit id="ErrorOpenLockBoldWasBlocked" translate="yes" xml:space="preserve">
<source>Bolds was or is blocked.</source>
<target state="new">Bolds was or is blocked.</target>
</trans-unit>
</group>
</body>
</file>

View file

@ -114,6 +114,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Bolds was or is blocked..
/// </summary>
internal static string ErrorOpenLockBoldWasBlocked {
get {
return ResourceManager.GetString("ErrorOpenLockBoldWasBlocked", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unexpected locking state &quot;{0}&quot; detected after sending open command..
/// </summary>
@ -122,14 +131,5 @@ namespace TINK.MultilingualResources {
return ResourceManager.GetString("ErrorOpenLockUnexpectedState", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lock reports unknown bold position..
/// </summary>
internal static string ErrorOpenLockUnknownPosition {
get {
return ResourceManager.GetString("ErrorOpenLockUnknownPosition", resourceCulture);
}
}
}
}

View file

@ -21,9 +21,6 @@
<data name="ErrorOpenLockUnexpectedState" xml:space="preserve">
<value>Unerwarteter Schlosszustand "{0}" gemeldet nach Ausführen des Öffnen-Befehls.</value>
</data>
<data name="ErrorOpenLockUnknownPosition" xml:space="preserve">
<value>Schloss meldet unbekannten Schließzustand.</value>
</data>
<data name="ErrorBluetoothDisconnectedException" xml:space="preserve">
<value>Keine Bluetooth-Verbindung.</value>
</data>

View file

@ -135,10 +135,10 @@
<data name="ErrorOpenLockBoldBlocked" xml:space="preserve">
<value>Bold is blocked.</value>
</data>
<data name="ErrorOpenLockBoldWasBlocked" xml:space="preserve">
<value>Bolds was or is blocked.</value>
</data>
<data name="ErrorOpenLockUnexpectedState" xml:space="preserve">
<value>Unexpected locking state "{0}" detected after sending open command.</value>
</data>
<data name="ErrorOpenLockUnknownPosition" xml:space="preserve">
<value>Lock reports unknown bold position.</value>
</data>
</root>

View file

@ -1,13 +0,0 @@
using TINK.Model.Bike.BluetoothLock;
namespace TINK.Services.BluetoothLock.Exception
{
public class CouldntOpenBoldBlockedException : StateAwareException
{
public CouldntOpenBoldBlockedException() : base(
LockingState.Unknown, //
MultilingualResources.Resources.ErrorOpenLockBoldBlocked)
{
}
}
}

View file

@ -0,0 +1,16 @@
using TINK.Model.Bike.BluetoothLock;
namespace TINK.Services.BluetoothLock.Exception
{
/// <summary>
/// Lock can not be opened, because bold is blocked. Lock reports that obstacle is still blocking.
/// </summary>
public class CouldntOpenBoldIsBlockedException : StateAwareException
{
public CouldntOpenBoldIsBlockedException() : base(
LockingState.Unknown,
MultilingualResources.Resources.ErrorOpenLockBoldBlocked)
{
}
}
}

View file

@ -0,0 +1,16 @@
using TINK.Model.Bike.BluetoothLock;
namespace TINK.Services.BluetoothLock.Exception
{
/// <summary>
/// Lock can not be opened, because bold is/ was blocked. Obstacle might no more block but lock could not be opened completely to obstacle.
/// </summary>
public class CouldntOpenBoldWasBlockedException : StateAwareException
{
public CouldntOpenBoldWasBlockedException() : base(
LockingState.Unknown,
MultilingualResources.Resources.ErrorOpenLockBoldWasBlocked)
{
}
}
}

View file

@ -9,9 +9,7 @@ namespace TINK.Services.BluetoothLock.Exception
public CouldntOpenInconsistentStateExecption(LockingState state) :
base(
state,
state != LockingState.Unknown
? string.Format(Resources.ErrorOpenLockUnexpectedState, state)
: Resources.ErrorOpenLockUnknownPosition)
string.Format(Resources.ErrorOpenLockUnexpectedState, state))
{
}
}

View file

@ -3,14 +3,71 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Sharee", "TINK\TINK\Sharee.shproj", "{5297504F-603F-4E1A-98AA-57C4A0D9D833}"
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TINK", "TINK\TINK\TINK.shproj", "{5297504F-603F-4E1A-98AA-57C4A0D9D833}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sharee.Android", "TINK\TINK.Android\Sharee.Android.csproj", "{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TINK.Android", "TINK\TINK.Android\TINK.Android.csproj", "{62B8950A-70B8-4F9D-AFFC-0A1EBE7BC9E7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sharee.iOS", "TINK\TINK.iOS\Sharee.iOS.csproj", "{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TINK.iOS", "TINK\TINK.iOS\TINK.iOS.csproj", "{F2D8208F-A8BF-4403-B0AE-2A1D270E4DC9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TINKLib", "TINKLib\TINKLib.csproj", "{B77F4222-0860-4494-A07C-EE8E09FA9983}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{49C8F824-4752-449E-A53C-35A2722AFA99}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set02 - Book 3rd bike", "Set02 - Book 3rd bike", "{50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}"
ProjectSection(SolutionItems) = preProject
TestData\Set02\Story.json = TestData\Set02\Story.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{A5D8D93B-4D4E-4C4C-A70C-44A451D6C722}"
ProjectSection(SolutionItems) = preProject
TestData\Set02\001\bikes_available.json = TestData\Set02\001\bikes_available.json
TestData\Set02\001\booking_request_bike_8.json = TestData\Set02\001\booking_request_bike_8.json
TestData\Set02\001\stations_all.json = TestData\Set02\001\stations_all.json
TestData\Set02\001\user_bikes_occupied.json = TestData\Set02\001\user_bikes_occupied.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "002", "002", "{D0A2A88B-5DD9-4B40-A8AE-2AED6FB41911}"
ProjectSection(SolutionItems) = preProject
TestData\Set02\002\bikes_available.json = TestData\Set02\002\bikes_available.json
TestData\Set02\002\stations_all.json = TestData\Set02\002\stations_all.json
TestData\Set02\002\user_bikes_occupied.json = TestData\Set02\002\user_bikes_occupied.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "003", "003", "{272FB1A5-BBC5-41DF-91C2-C1C1A85AFD44}"
ProjectSection(SolutionItems) = preProject
TestData\Set02\003\bikes_available.json = TestData\Set02\003\bikes_available.json
TestData\Set02\003\stations_all.json = TestData\Set02\003\stations_all.json
TestData\Set02\003\user_bikes_occupied.json = TestData\Set02\003\user_bikes_occupied.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set03 - Book 1st bike", "Set03 - Book 1st bike", "{E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61}"
ProjectSection(SolutionItems) = preProject
TestData\Set03\Story.json = TestData\Set03\Story.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{B0BE116C-B05C-4864-9D1F-5CE1A8EC7BD0}"
ProjectSection(SolutionItems) = preProject
TestData\Set03\001\booking_request_bike_20.json = TestData\Set03\001\booking_request_bike_20.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "002", "002", "{C579CA91-17DC-4AC4-8F1B-377A245883FD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set01 - Log in", "Set01 - Log in", "{A872956F-17F0-416E-9A9A-F28D96F13A94}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{104F18F2-1261-42C0-96A4-F5BBACF595DA}"
ProjectSection(SolutionItems) = preProject
TestData\Set01\001\authorization_javaminister%40gmail.com_javaminister_HwId1000000000000.json = TestData\Set01\001\authorization_javaminister%40gmail.com_javaminister_HwId1000000000000.json
TestData\Set01\001\bikes_available.json = TestData\Set01\001\bikes_available.json
TestData\Set01\001\bikes_occupied.json = TestData\Set01\001\bikes_occupied.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Set04 - Cancel Booking", "Set04 - Cancel Booking", "{55C5EF2A-FDEB-40F6-ABE9-84B7B1981B2B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "001", "001", "{AADA3B61-8626-43AE-BED9-BA5AA3D93576}"
ProjectSection(SolutionItems) = preProject
TestData\Set04 - Cancel Booking\001\booking_cancel_bike_7.json = TestData\Set04 - Cancel Booking\001\booking_cancel_bike_7.json
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LockItShared", "LockItShared\LockItShared.csproj", "{3589ED1D-E734-429D-976F-1BEA4371DF14}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LockItBLE", "LockItBLE\LockItBLE.csproj", "{BDE9CE26-15CF-47DA-A4F6-B6956D02D0FC}"
@ -451,6 +508,19 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0} = {49C8F824-4752-449E-A53C-35A2722AFA99}
{A5D8D93B-4D4E-4C4C-A70C-44A451D6C722} = {50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}
{D0A2A88B-5DD9-4B40-A8AE-2AED6FB41911} = {50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}
{272FB1A5-BBC5-41DF-91C2-C1C1A85AFD44} = {50D4A63A-91D8-4EC5-ADF6-9EF72D7AFAF0}
{E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61} = {49C8F824-4752-449E-A53C-35A2722AFA99}
{B0BE116C-B05C-4864-9D1F-5CE1A8EC7BD0} = {E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61}
{C579CA91-17DC-4AC4-8F1B-377A245883FD} = {E5E0E87A-1CDD-4E66-AF66-26EBDD0C6E61}
{A872956F-17F0-416E-9A9A-F28D96F13A94} = {49C8F824-4752-449E-A53C-35A2722AFA99}
{104F18F2-1261-42C0-96A4-F5BBACF595DA} = {A872956F-17F0-416E-9A9A-F28D96F13A94}
{55C5EF2A-FDEB-40F6-ABE9-84B7B1981B2B} = {49C8F824-4752-449E-A53C-35A2722AFA99}
{AADA3B61-8626-43AE-BED9-BA5AA3D93576} = {55C5EF2A-FDEB-40F6-ABE9-84B7B1981B2B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C6529CD7-C3F7-4E80-89B5-002E2B8E3EB5}
EndGlobalSection

View file

@ -1,16 +1,23 @@
using TINK.Model.Device;
using Xamarin.Essentials;
using Xamarin.Forms;
[assembly: Dependency(typeof(TINK.Droid.Model.Device.Device))]
namespace TINK.Droid.Model.Device
{
public class Device : IDevice
public class Device : ISmartDevice
{
public string Manufacturer => DeviceInfo.Manufacturer;
public string Model => DeviceInfo.Model;
public string PlatformText => DeviceInfo.Platform.ToString();
public string VersionText => DeviceInfo.VersionString;
/// <summary> Gets unitque device identifier. </summary>
/// <returns>Gets the identifies specifying device.</returns>
public string GetIdentifier()
{
return Android.Provider.Settings.Secure.GetString(Forms.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
}
public string Identifier
=> Android.Provider.Settings.Secure.GetString(Forms.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.222" android:versionCode="222">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.238" android:versionCode="238">
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="30" />
<!-- Google Maps related permissions -->
<permission android:name="com.ecs.google.maps.v2.actionbarsherlock.permission.MAPS_RECEIVE" android:protectionLevel="signature" />
@ -15,6 +15,6 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<application android:icon="@drawable/sharee" android:label="sharee.bike"></application>
<application android:icon="@drawable/sharee" android:label="sharee.bike" android:allowBackup="false"></application>
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="000000000-000000000000000000000000000-0" />
</manifest>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.222" android:versionCode="222">
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="30" />
<!-- Google Maps related permissions -->
<permission android:name="com.ecs.google.maps.v2.actionbarsherlock.permission.MAPS_RECEIVE" android:protectionLevel="signature" />
<!-- Network connectivity permissions -->
<!-- Access Google based webservices -->
<!-- External storage for caching. -->
<!-- My Location -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<application android:icon="@drawable/sharee" android:label="sharee.bike"></application>
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="000000000-000000000000000000000000000-0" />
</manifest>

File diff suppressed because it is too large Load diff

View file

@ -30,7 +30,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;USESHELL</DefineConstants>
<DefineConstants>TRACE;DEBUG;USEFLYOUT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>0</WarningLevel>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
@ -49,7 +49,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;USEFLYOUT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
@ -63,16 +63,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.1" />
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.2" />
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
<PackageReference Include="MonkeyCache">
<Version>1.3.0</Version>
<Version>1.5.2</Version>
</PackageReference>
<PackageReference Include="MonkeyCache.FileStore">
<Version>1.3.0</Version>
<Version>1.5.2</Version>
</PackageReference>
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="PCLCrypto" Version="2.0.147" />
<PackageReference Include="PCLStorage" Version="1.0.2" />
<PackageReference Include="PInvoke.BCrypt" Version="0.7.104" />
@ -161,11 +161,17 @@
<PackageReference Include="Xamarin.Android.Support.v7.Palette" Version="28.0.0.3" />
<PackageReference Include="Xamarin.Android.Support.v7.RecyclerView" Version="28.0.0.3" />
<PackageReference Include="Xamarin.Android.Support.Vector.Drawable" Version="28.0.0.3" />
<PackageReference Include="Xamarin.AndroidX.Core">
<Version>1.5.0</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.MediaRouter">
<Version>1.2.1</Version>
<Version>1.2.3</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.Palette">
<Version>1.0.0.6</Version>
<Version>1.0.0.7</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.RecyclerView">
<Version>1.2.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Auth" Version="1.7.0" />
<PackageReference Include="Xamarin.Build.Download" Version="0.10.0" />
@ -182,7 +188,7 @@
<PackageReference Include="Xamarin.Forms.GoogleMaps.Bindings" Version="2.2.0" />
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0" />
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="117.6.0" />
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.0" />
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="117.0.1" />
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="117.2.1" />
</ItemGroup>
<ItemGroup>

View file

@ -1,8 +0,0 @@
<Application
x:Class="TINK.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TINK.UWP"
RequestedTheme="Light">
</Application>

View file

@ -1,107 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace TINK.UWP
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}
#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Xamarin.Forms.Forms.Init(e);
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -1,32 +0,0 @@
using System;
using TINK.Model.Device;
using TINK.UWP.Device;
using Windows.System.Profile;
using Windows.UI.Xaml;
[assembly: Xamarin.Forms.Dependency(typeof(WinPhoneDevice))]
namespace TINK.UWP.Device
{
public class WinPhoneDevice : IDevice
{
/// <summary> Gets unitque device identifier. </summary>
/// <returns>Gets the identifies specifying device.</returns>
public string GetIdentifier()
{
var token = HardwareIdentification.GetPackageSpecificToken(null);
var hardwareId = token.Id;
var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);
byte[] bytes = new byte[hardwareId.Length];
dataReader.ReadBytes(bytes);
return BitConverter.ToString(bytes);
}
/// <summary> Close the application. </summary>
public void CloseApplication()
{
Application.Current.Exit();
}
}
}

View file

@ -1,15 +0,0 @@
<forms:WindowsPage
x:Class="TINK.UWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:forms="using:Xamarin.Forms.Platform.UWP"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TINK.UWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
</Grid>
</forms:WindowsPage>

View file

@ -1,19 +0,0 @@
namespace TINK.UWP
{
public sealed partial class MainPage
{
public MainPage()
{
this.InitializeComponent();
// Todo: Token hier eingeben
// Required for initialization of Maps, see https://developer.xamarin.com/guides/xamarin-forms/user-interface/map/
Xamarin.FormsGoogleMaps.Init("AIzaSyDOd-_356QgShjVGUPGH1LatYJKWdzk9-U");
// Required for initialization of binding package, see https://github.com/nuitsjp/Xamarin.Forms.GoogleMaps.Bindings.
Xamarin.FormsGoogleMapsBindings.Init();
LoadApplication(new TINK.App());
}
}
}

View file

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
IgnorableNamespaces="uap mp">
<Identity
Name="4fa9c991-fff2-493e-b399-75bf6354090c"
Publisher="CN=Oliver"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="f736c883-f105-4d30-a719-4bf328872f5e" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>TINK.UWP</DisplayName>
<PublisherDisplayName>Oliver</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="TINK.UWP.App">
<uap:VisualElements
DisplayName="TINK.UWP"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="TINK.UWP"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
</Capabilities>
</Package>

View file

@ -1,29 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TINK.UWP")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TINK.UWP")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]

View file

@ -1,31 +0,0 @@
<!--
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
developers. However, you can modify these parameters to modify the behavior of the .NET Native
optimizer.
Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919
To fully enable reflection for App1.MyClass and all of its public/private members
<Type Name="App1.MyClass" Dynamic="Required All"/>
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
<Namespace Name="DataClasses.ViewModels" Seralize="All" />
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!--
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
</Application>
</Directives>

View file

@ -1,171 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{B3DC74E1-DA60-4844-BB25-A7F6D2BB6A9D}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TINK.UWP</RootNamespace>
<AssemblyName>TINK.UWP</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.15063.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.10586.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<EnableDotNetNativeCompatibleProfile>true</EnableDotNetNativeCompatibleProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>TINK.UWP_TemporaryKey.pfx</PackageCertificateKeyFile>
<ReleaseVersion>3.0</ReleaseVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Device\WinPhoneDevice .cs" />
<Compile Include="MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<None Include="TINK.UWP_TemporaryKey.pfx" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\tink2.png" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-100.png" />
<Content Include="Assets\LockScreenLogo.scale-125.png" />
<Content Include="Assets\LockScreenLogo.scale-150.png" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\LockScreenLogo.scale-400.png" />
<Content Include="Assets\SplashScreen.scale-100.png" />
<Content Include="Assets\SplashScreen.scale-125.png" />
<Content Include="Assets\SplashScreen.scale-150.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-400.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-100.png" />
<Content Include="Assets\Square44x44Logo.scale-125.png" />
<Content Include="Assets\Square44x44Logo.scale-150.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-400.png" />
<Content Include="Assets\Square44x44Logo.targetsize-16_altform-unplated.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\Square44x44Logo.targetsize-32_altform-unplated.png" />
<Content Include="Assets\Square44x44Logo.targetsize-48_altform-unplated.png" />
<Content Include="Assets\Square44x44Logo.targetsize-256_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-100.png" />
<Content Include="Assets\Wide310x150Logo.scale-125.png" />
<Content Include="Assets\Wide310x150Logo.scale-150.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
<Content Include="Assets\Wide310x150Logo.scale-400.png" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\TINKLib\TINKLib.csproj">
<Project>{b77f4222-0860-4494-a07c-ee8e09fa9983}</Project>
<Name>TINKLib</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="..\TINK\TINK.projitems" Label="Shared" Condition="Exists('..\TINK\TINK.projitems')" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -1,28 +0,0 @@
{
"dependencies": {
"Microsoft.Net.Http": "2.2.29",
"Microsoft.NETCore.UniversalWindowsPlatform": "6.1.4",
"PCLStorage": "1.0.2",
"Serilog": "2.7.1",
"Serilog.Sinks.File": "4.0.0",
"System.Collections.Immutable": "1.4.0",
"System.Private.DataContractSerialization": "4.3.0",
"System.Runtime.Serialization.Formatters": "4.3.0",
"System.Runtime.Serialization.Primitives": "4.3.0",
"Xam.Plugins.Messaging": "5.2.0",
"Xamarin.Forms": "3.0.0.482510",
"Xamarin.Forms.GoogleMaps": "2.3.0",
"Xamarin.Forms.GoogleMaps.Bindings": "2.1.0"
},
"frameworks": {
"uap10.0.10586": {}
},
"runtimes": {
"win10-arm": {},
"win10-arm-aot": {},
"win10-x86": {},
"win10-x86-aot": {},
"win10-x64": {},
"win10-x64-aot": {}
}
}

View file

@ -1,11 +1,12 @@
using System;
using System.Runtime.InteropServices;
using TINK.Model.Device;
using Xamarin.Essentials;
[assembly: Xamarin.Forms.Dependency(typeof(TINK.iOS.Device.Device))]
namespace TINK.iOS.Device
{
public class Device : IDevice
public class Device : ISmartDevice
{
[DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
private static extern uint IOServiceGetMatchingService(uint masterPort, IntPtr matching);
@ -19,11 +20,16 @@ namespace TINK.iOS.Device
[DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
private static extern int IOObjectRelease(uint o);
public string Manufacturer => DeviceInfo.Manufacturer;
public string Model => DeviceInfo.Model;
public string PlatformText => DeviceInfo.Platform.ToString();
public string VersionText => DeviceInfo.VersionString;
/// <summary> Gets unitque device identifier. </summary>
/// <returns>Gets the identifies specifying device.</returns>
public string GetIdentifier()
{
return UIKit.UIDevice.CurrentDevice?.IdentifierForVendor?.AsString() ?? string.Empty;
}
public string Identifier
=> UIKit.UIDevice.CurrentDevice?.IdentifierForVendor?.AsString() ?? string.Empty;
}
}

View file

@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.TeilRad.sharee.bike</string>
</array>
</dict>
</plist>

View file

@ -49,8 +49,8 @@
<key>CFBundleDisplayName</key>
<string>sharee.bike</string>
<key>CFBundleVersion</key>
<string>222</string>
<string>238</string>
<key>CFBundleShortVersionString</key>
<string>3.0.222</string>
<string>3.0.238</string>
</dict>
</plist>

View file

@ -47,7 +47,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>__IOS__;__MOBILE__;__UNIFIED__;DEBUG;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT</DefineConstants>
<DefineConstants>DEBUG;USEFLYOUT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
@ -61,7 +61,7 @@
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<DefineConstants>__IOS__;__MOBILE__;__UNIFIED__;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT</DefineConstants>
<DefineConstants>USEFLYOUT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchArch>ARMv7, ARM64</MtouchArch>
@ -78,7 +78,7 @@
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\Ad-Hoc</OutputPath>
<DefineConstants>__IOS__;__MOBILE__;__UNIFIED__;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT</DefineConstants>
<DefineConstants>USEFLYOUT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
@ -94,7 +94,7 @@
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\AppStore</OutputPath>
<DefineConstants>__IOS__;__MOBILE__;__UNIFIED__;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT</DefineConstants>
<DefineConstants>USEFLYOUT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
@ -109,16 +109,16 @@
<ItemGroup>
<PackageReference Include="Microsoft.Bcl.Build" Version="1.0.21" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.1" />
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.2" />
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
<PackageReference Include="MonkeyCache">
<Version>1.3.0</Version>
<Version>1.5.2</Version>
</PackageReference>
<PackageReference Include="MonkeyCache.FileStore">
<Version>1.3.0</Version>
<Version>1.5.2</Version>
</PackageReference>
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="PCLCrypto" Version="2.0.147" />
<PackageReference Include="PCLStorage" Version="1.0.2" />
<PackageReference Include="Plugin.BluetoothLE">

View file

@ -17,6 +17,10 @@ using System.Threading;
using TINK.Model.Settings;
using Plugin.Permissions;
using TINK.Services.BluetoothLock.Crypto;
using TINK.Model.Services.Geolocation;
using TINK.Services;
using System.Threading.Tasks;
using Xamarin.Essentials;
#if ARENDI
using Arendi.BleLibrary.Local;
#endif
@ -45,38 +49,100 @@ namespace TINK
// Root model already exists, nothing to do.
return m_oModelRoot;
}
// Get folder where to read settings from
var specialFolders = DependencyService.Get<ISpecialFolder>();
var internalPersonalDir = specialFolders.GetInternalPersonalDir();
// Delete attachtment from previous session.
DeleteAttachment(internalPersonalDir);
// Setup logger using default settings.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(new LoggingLevelSwitch { MinimumLevel = Model.Settings.Settings.DEFAULTLOGGINLEVEL })
.WriteTo.Debug()
.WriteTo.File(internalPersonalDir, Model.Logging.RollingInterval.Session)
.CreateLogger();
// Subscribe to any unhandled/ unobserved exceptions.
AppDomain.CurrentDomain.UnhandledException += (sender, unobservedTaskExceptionEventArgs) => { Log.Fatal("Unobserved task exception: {Exception}", unobservedTaskExceptionEventArgs.ExceptionObject); };
TaskScheduler.UnobservedTaskException += (sender, unhandledExceptionEventArgs) => { Log.Fatal("Unhandled exception: {Exception}", unhandledExceptionEventArgs.Exception); };
// Restore last model state from json- file.
Dictionary<string, string> settingsJSON = new Dictionary<string, string>();
try
{
settingsJSON = JsonSettingsDictionary.Deserialize(specialFolders.GetInternalPersonalDir());
settingsJSON = JsonSettingsDictionary.Deserialize(internalPersonalDir);
}
catch (Exception l_oException)
catch (Exception exception)
{
Log.Error("Reading application settings from file failed.", l_oException);
Log.Error("Reading application settings from file failed.", exception);
}
var settings = new Model.Settings.Settings(
JsonSettingsDictionary.GetGroupFilterMapPage(settingsJSON),
JsonSettingsDictionary.GetGoupFilterSettings(settingsJSON),
JsonSettingsDictionary.GetCopriHostUri(settingsJSON),
JsonSettingsDictionary.GetPollingParameters(settingsJSON),
JsonSettingsDictionary.GetMinimumLoggingLevel(settingsJSON),
JsonSettingsDictionary.GetExpiresAfter(settingsJSON),
JsonSettingsDictionary.GetActiveLockService(settingsJSON),
JsonSettingsDictionary.GetConnectTimeout(settingsJSON),
JsonSettingsDictionary.GetActiveGeolocationService(settingsJSON),
JsonSettingsDictionary.GetCenterMapToCurrentLocation(settingsJSON),
JsonSettingsDictionary.GetLogToExternalFolder(settingsJSON),
JsonSettingsDictionary.GetIsSiteCachingOn(settingsJSON),
JsonSettingsDictionary.GetActiveTheme(settingsJSON));
Model.Settings.Settings settings;
try
{
settings = new Model.Settings.Settings(
JsonSettingsDictionary.GetGroupFilterMapPage(settingsJSON),
JsonSettingsDictionary.GetGoupFilterSettings(settingsJSON),
JsonSettingsDictionary.GetCopriHostUri(settingsJSON),
JsonSettingsDictionary.GetPollingParameters(settingsJSON),
JsonSettingsDictionary.GetMinimumLoggingLevel(settingsJSON),
JsonSettingsDictionary.GetIsReportLevelVerbose(settingsJSON),
JsonSettingsDictionary.GetExpiresAfter(settingsJSON),
JsonSettingsDictionary.GetActiveLockService(settingsJSON),
JsonSettingsDictionary.GetConnectTimeout(settingsJSON),
JsonSettingsDictionary.GetActiveGeolocationService(settingsJSON),
JsonSettingsDictionary.GetCenterMapToCurrentLocation(settingsJSON),
JsonSettingsDictionary.GetLogToExternalFolder(settingsJSON),
JsonSettingsDictionary.GetIsSiteCachingOn(settingsJSON),
JsonSettingsDictionary.GetActiveTheme(settingsJSON));
}
catch (Exception exception)
{
Log.Error("Deserializing application settings from dictionary failed.", exception);
settings = new Model.Settings.Settings();
}
var store = new Store(settings.ActiveUri.GetHashCode().ToString());
var l_oAccount = new Account(store.Load());
if (settings.MinimumLogEventLevel != Model.Settings.Settings.DEFAULTLOGGINLEVEL
|| settings.LogToExternalFolder)
{
// Eigher
// - logging is not set to default value or
// - logging is performed to external folder.
// Need to reconfigure.
Log.CloseAndFlush(); // Close before modifying logger configuration. Otherwise a sharing vialation occurs.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(new LoggingLevelSwitch(settings.MinimumLogEventLevel))
.WriteTo.Debug()
.WriteTo.File(!settings.LogToExternalFolder ? internalPersonalDir : specialFolders.GetExternalFilesDir(), Model.Logging.RollingInterval.Session)
.CreateLogger();
}
// Get auth cookie
Log.Debug("Get auth cookie.");
IStore store = null;
var lastVersion = JsonSettingsDictionary.GetAppVersion(settingsJSON);
if (lastVersion > new Version(3, 0, 173))
GeolocationServicesContainer.SetActive(settings.ActiveGeolocationService);
if (new Version(0, 0, 0) < lastVersion
&& lastVersion <= new Version(3, 0, 234))
{
// Version 3.0.245 and older used Xamarin.Auth.AccountStore to securely store data.
// Later version s use Xamarin.Essentials Secure Storage.
store = new StoreLegacy(settings.ActiveUri.GetHashCode().ToString());
}
else
{
// Either
// - frist install or
// - version whitch uses secure storage
// detected.
store = new Store();
}
Barrel.ApplicationId = "TINKApp";
@ -85,17 +151,17 @@ namespace TINK
var appInfoService = DependencyService.Get<IAppInfo>();
// Create new app instnace.
Log.Debug("Constructing main model...");
m_oModelRoot = new TinkApp(
settings,
store, // Manages user account
(isConnected, activeUri, sessionCookie, mail, expiresAfter) => ConnectorFactory.Create(isConnected, activeUri, $"TINKApp/{appInfoService.Version}", sessionCookie, mail, expiresAfter),
null, /* geolocationService */
DependencyService.Get<IGeolodationDependent>(),
(isConnected, activeUri, sessionCookie, mail, expiresAfter) => ConnectorFactory.Create(isConnected, activeUri, $"sharee.bike/{appInfoService.Version}", sessionCookie, mail, expiresAfter),
GeolocationServicesContainer,
null, /* locksService */
DependencyService.Get<IDevice>(),
DependencyService.Get<ISmartDevice>(),
specialFolders,
new Cipher(),
CrossPermissions.Current,
null, // Permissions, no more used.
#if ARENDI
DependencyService.Get<ICentral>(),
#else
@ -107,6 +173,7 @@ namespace TINK
lastVersion: JsonSettingsDictionary.GetAppVersion(settingsJSON),
whatsNewShownInVersion: JsonSettingsDictionary.GetWhatsNew(settingsJSON) ?? settingsJSON.GetAppVersion());
Log.Debug("Main model successfully constructed.");
return m_oModelRoot;
}
}
@ -123,7 +190,7 @@ namespace TINK
MainPage = ModelRoot.WhatsNew.IsShowRequired
? new View.WhatsNew.WhatsNewPage(() => MainPage = new View.MainPage()) // Show whats new info.
: (Page) new View.MainPage(); // Just use TINKApp
#elif USEFLYOU
#elif USEFLYOUT
// Use flyout page.
MainPage = ModelRoot.WhatsNew.IsShowRequired
? new View.WhatsNew.WhatsNewPage(() => MainPage = new View.Root.RootPage()) // Show whats new info.
@ -138,12 +205,12 @@ namespace TINK
}
/// <summary> Concatenates all log files to a single one. </summary>
/// <returns>File name of attachment.</returns>
/// <returns>Full file name of attachment.</returns>
public static string CreateAttachment()
{
var l_oLogFiles = Log.Logger.GetLogFiles().ToArray();
var sessionLogFiles = Log.Logger.GetLogFiles().ToArray();
if (l_oLogFiles.Length < 1)
if (sessionLogFiles.Length < 1)
{
// Either
// - there is no logging file
@ -151,14 +218,14 @@ namespace TINK
return string.Empty;
}
var l_oLogPath = System.IO.Path.Combine(ModelRoot.LogFileParentFolder, ATTACHMENTTITLE);
var fullLogFileName = System.IO.Path.Combine(ModelRoot.LogFileParentFolder, ATTACHMENTTITLE);
// Stop logging to avoid file access exception.
Log.CloseAndFlush();
System.IO.File.WriteAllLines(
l_oLogPath,
l_oLogFiles.SelectMany(name =>
fullLogFileName,
sessionLogFiles.SelectMany(name =>
(new List<string> { $"{{\"SessionFileName\":\"{name}\"}}" })
.Concat(System.IO.File.ReadLines(name).ToArray())));
@ -169,33 +236,21 @@ namespace TINK
.WriteTo.File(ModelRoot.LogFileParentFolder, Model.Logging.RollingInterval.Session)
.CreateLogger();
return l_oLogPath;
return fullLogFileName;
}
/// <summary>Deletes an attachment if there is one.</summary>
private static void DeleteAttachment()
/// <param name="folder">Folder to delete, is null folder is queried from model.</param>
private static void DeleteAttachment(string folder = null)
{
var l_oAttachment = System.IO.Path.Combine(ModelRoot.LogFileParentFolder, ATTACHMENTTITLE);
if (!System.IO.File.Exists(l_oAttachment))
var attachment = System.IO.Path.Combine(folder ?? ModelRoot.LogFileParentFolder, ATTACHMENTTITLE);
if (!System.IO.File.Exists(attachment))
{
// No attachment found.
return;
}
System.IO.File.Delete(l_oAttachment);
}
/// <summary> TINK app starts up.</summary>
protected override void OnStart()
{
DeleteAttachment();
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(new LoggingLevelSwitch { MinimumLevel = ModelRoot.Level.MinimumLevel })
.WriteTo.Debug()
.WriteTo.File(ModelRoot.LogFileParentFolder, Model.Logging.RollingInterval.Session)
.CreateLogger();
System.IO.File.Delete(attachment);
}
protected override void OnSleep()
@ -227,5 +282,26 @@ namespace TINK
return LogEventLevel.Error;
}
/// <summary>
/// Service to manage permissions (location) of the app.
/// </summary>
public static Plugin.Permissions.Abstractions.IPermissions PermissionsService => CrossPermissions.Current;
/// <summary>
/// Service to manage bluetooth stack.
/// </summary>
public static Plugin.BLE.Abstractions.Contracts.IBluetoothLE BluetoothService => Plugin.BLE.CrossBluetoothLE.Current;
/// <summary>
/// Service container to manage geolocation services.
/// </summary>
public static IServicesContainer<IGeolocation> GeolocationServicesContainer { get; }
= new Services.ServicesContainerMutable<Model.Services.Geolocation.IGeolocation>(
new HashSet<Model.Services.Geolocation.IGeolocation> {
new LastKnownGeolocationService(DependencyService.Get<IGeolodationDependent>()),
new SimulatedGeolocationService(DependencyService.Get<IGeolodationDependent>()),
new GeolocationService(DependencyService.Get<IGeolodationDependent>()) },
typeof(LastKnownGeolocationService).FullName);
}
}

View file

@ -7,19 +7,19 @@ namespace TINK
{
public static class BackdoorMethodHelpers
{
public static void DoTapPage(string stationIndex )
public static void DoTapPage(string stationId )
{
Serilog.Log.Information($"Request via backdoor to tap station {stationIndex}.");
Serilog.Log.Information($"Request via backdoor to tap station {stationId}.");
var currentPage = GetCurrentPage();
var mapPageViewModel = (currentPage as MapPage)?.BindingContext as MapPageViewModel;
if (mapPageViewModel == null)
{
Serilog.Log.Error($"Request via backdoor to tap station {stationIndex} aborted because current page is not of expected type {typeof(MapPage).Name}. Type detected is {currentPage.GetType().Name}.");
Serilog.Log.Error($"Request via backdoor to tap station {stationId} aborted because current page is not of expected type {typeof(MapPage).Name}. Type detected is {currentPage.GetType().Name}.");
return;
}
Serilog.Log.Information($"Invoking member to tap.");
mapPageViewModel?.OnStationClicked(int.Parse(stationIndex));
mapPageViewModel?.OnStationClicked(stationId);
}
/// <summary> Gets the current page assumed that app is master detail page.</summary>

View file

@ -1,11 +1,13 @@
using System;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TINK.Model.Connector;
namespace TINK.Model.User.Account
{
public class Store : IStore
public class StoreLegacy : IStore
{
/// <summary>
/// Holds the name of the application.
@ -21,12 +23,9 @@ namespace TINK.Model.User.Account
/// <summary> Holds the id of the session. </summary>
private const string KEY_DEBUGLEVEL = "DebugLevel";
private Store()
{ }
public Store(string p_strCopriHostHash)
public StoreLegacy(string copriHostHash)
{
m_strCopriHostHash = p_strCopriHostHash
m_strCopriHostHash = copriHostHash
?? throw new ArgumentException("Can not construct account object. Copri hash must not be null.");
}
@ -36,26 +35,26 @@ namespace TINK.Model.User.Account
/// Reads mail address and password from account store.
/// </summary>
/// <returns></returns>
public IAccount Load()
public async Task<IAccount> Load()
{
#if !WINDOWS_UWP
#if !__IOS__
var account = Xamarin.Auth.AccountStore.Create("System.Char[]").FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
var xamAccountStore = Xamarin.Auth.AccountStore.Create("System.Char[]").FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
#else
var account = Xamarin.Auth.AccountStore.Create().FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
var xamAccountStore = Xamarin.Auth.AccountStore.Create().FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
#endif
if (account == null)
if (xamAccountStore == null)
{
// Nothing t do if account cannot be accessed.
return new EmptyAccount();
}
return new Account(
account.Username,
xamAccountStore.Username,
string.Empty,
account.Properties.ContainsKey(KEY_SESSIONID) ? account.Properties[KEY_SESSIONID] : null,
account.Properties.ContainsKey(KEY_GROUP) ? TextToTypeHelper.GetGroup(account.Properties[KEY_GROUP]) : new List<string>(),
account.Properties.ContainsKey(KEY_DEBUGLEVEL) && !string.IsNullOrEmpty(account.Properties[KEY_DEBUGLEVEL]) ? Permissions.Parse<Permissions>(account.Properties[KEY_DEBUGLEVEL]) : Permissions.None);
xamAccountStore.Properties.ContainsKey(KEY_SESSIONID) ? xamAccountStore.Properties[KEY_SESSIONID] : null,
xamAccountStore.Properties.ContainsKey(KEY_GROUP) && !string.IsNullOrEmpty(xamAccountStore.Properties[KEY_GROUP]) ? JsonConvert.DeserializeObject<IEnumerable<string>>(xamAccountStore.Properties[KEY_GROUP]) : new string[0],
xamAccountStore.Properties.ContainsKey(KEY_DEBUGLEVEL) && !string.IsNullOrEmpty(xamAccountStore.Properties[KEY_DEBUGLEVEL]) ? Permissions.Parse<Permissions>(xamAccountStore.Properties[KEY_DEBUGLEVEL]) : Permissions.None);
#else
return new Account(
string.Empty,
@ -70,22 +69,22 @@ namespace TINK.Model.User.Account
/// <summary>
/// Writes mail address and password to account store.
/// </summary>
/// <param name="p_oAccount"></param>
public void Save(IAccount p_oAccount)
/// <param name="account"></param>
public async Task Save(IAccount account)
{
#if !WINDOWS_UWP
Xamarin.Auth.Account account = new Xamarin.Auth.Account
Xamarin.Auth.Account xamAccount = new Xamarin.Auth.Account
{
Username = p_oAccount.Mail
Username = account.Mail
};
account.Properties.Add(KEY_SESSIONID, p_oAccount?.SessionCookie);
account.Properties.Add(KEY_GROUP, p_oAccount?.Group?.GetGroup());
account.Properties.Add(KEY_DEBUGLEVEL, p_oAccount.DebugLevel.ToString());
xamAccount.Properties.Add(KEY_SESSIONID, account?.SessionCookie);
xamAccount.Properties.Add(KEY_GROUP, JsonConvert.SerializeObject(account?.Group ?? new string[0]));
xamAccount.Properties.Add(KEY_DEBUGLEVEL, account.DebugLevel.ToString());
#if !__IOS__
Xamarin.Auth.AccountStore.Create("System.Char[]").Save(account, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
Xamarin.Auth.AccountStore.Create("System.Char[]").Save(xamAccount, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
#else
Xamarin.Auth.AccountStore.Create().Save(account, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
Xamarin.Auth.AccountStore.Create().Save(xamAccount, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
#endif
#endif
}
@ -93,26 +92,25 @@ namespace TINK.Model.User.Account
/// <summary>
/// Deletes mail address and password from account store.
/// </summary>
public IAccount Delete(IAccount p_oAccount)
public IAccount Delete(IAccount account)
{
#if !WINDOWS_UWP
#if !__IOS__
var account = Xamarin.Auth.AccountStore.Create("System.Char[]").FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
var xamAccountStore = Xamarin.Auth.AccountStore.Create("System.Char[]").FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
#else
var account = Xamarin.Auth.AccountStore.Create().FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
var xamAccountStore = Xamarin.Auth.AccountStore.Create().FindAccountsForService($"{M_STR_APPNAME}_{m_strCopriHostHash}").FirstOrDefault();
#endif
if (account == null)
if (xamAccountStore == null)
{
return new EmptyAccount();
}
#if !__IOS__
Xamarin.Auth.AccountStore.Create("System.Char[]").Delete(account, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
Xamarin.Auth.AccountStore.Create("System.Char[]").Delete(xamAccountStore, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
#else
Xamarin.Auth.AccountStore.Create().Delete(account, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
Xamarin.Auth.AccountStore.Create().Delete(xamAccountStore, $"{M_STR_APPNAME}_{m_strCopriHostHash}");
#endif
#endif
return new EmptyAccount();
}
}
}

View file

@ -14,7 +14,6 @@
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)BackdoorMethodHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\BluetoothLock\Arendi\Central.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\FeesAndBikes\HelpContactViewModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\RootShell\AppShellViewModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\RootFlyout\RootPageViewModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)View\Account\AccountPage.xaml.cs">
@ -76,6 +75,7 @@
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)View\WhatsNew\WhatsNewPage.xaml.cs">
<DependentUpon>WhatsNewPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Model\User\Account\Store.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\ViewModelResourceHelper.cs" />

View file

@ -2,7 +2,9 @@
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using System;
using TINK.Model.Device;
using TINK.ViewModel.Account;
@ -10,7 +12,11 @@ using TINK.ViewModel.Account;
namespace TINK.View.Account
{
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
public partial class AccountPage : ContentPage, IViewService, IDetailPage
#else
public partial class AccountPage : ContentPage, IViewService
#endif
{
/// <summary> Refernce to view model. </summary>
AccountPageViewModel m_oViewModel = null;
@ -31,18 +37,18 @@ namespace TINK.View.Account
}
/// <summary> Displays altert message. </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAlert(string title, string message, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, message, cancel);
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
@ -50,32 +56,48 @@ namespace TINK.View.Account
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays alert message.</summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
/// <summary>
/// Displays an action sheet.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="cancel">Text of button.</param>
/// <param name="destruction"></param>
/// <param name="p_oButtons">Buttons holding options to select.</param>
/// <returns>Text selected</returns>
public new async Task<string> DisplayActionSheet(String p_strTitle, String p_strCancel, String destruction, params String[] p_oButtons)
=> await base.DisplayActionSheet(p_strTitle, p_strCancel, destruction, p_oButtons);
public new async Task<string> DisplayActionSheet(String title, String cancel, String destruction, params String[] p_oButtons)
=> await base.DisplayActionSheet(title, cancel, destruction, p_oButtons);
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
public void ShowPage(ViewTypes p_oType, string title = null)
=> m_oNavigation.ShowPage(p_oType.GetViewType(), title);
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
@ -86,6 +108,7 @@ namespace TINK.View.Account
public Task PopModalAsync()
=> throw new NotSupportedException();
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigation;
@ -98,6 +121,7 @@ namespace TINK.View.Account
set { m_oNavigation = value; }
}
#endif
/// <summary>
/// Invoked when page is shown.
/// Starts update process.
@ -122,7 +146,10 @@ namespace TINK.View.Account
/// <param name="p_oTypeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes p_oTypeOfPage)
=> await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
#if USCSHARP9
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#else
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
}
}

View file

@ -15,7 +15,14 @@
Padding="10">
<Label
FontAttributes="Bold"
FontSize="Large"
HorizontalTextAlignment="Center"
Text="{Binding Name}"/>
<Label
FontAttributes="Bold"
HorizontalTextAlignment="Center"
IsVisible="{Binding DisplayId, Converter={StaticResource Label_Converter}}"
Text="{Binding DisplayId}"/>
<Label
Text="{Binding StateText}"
TextColor="{Binding StateColor}"/>

View file

@ -10,7 +10,9 @@ namespace TINK.View.BikesAtStation
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Device;
using TINK.View.MasterDetail;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel;
using TINK.Model;
using TINK.Services.BluetoothLock.Tdo;
@ -23,7 +25,11 @@ namespace TINK.View.BikesAtStation
using Xamarin.CommunityToolkit.Extensions;
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
public partial class BikesAtStationPage : ContentPage, IViewService, IDetailPage
#else
public partial class BikesAtStationPage : ContentPage, IViewService
#endif
{
private BikesAtStationPageViewModel m_oViewModel;
@ -78,23 +84,27 @@ namespace TINK.View.BikesAtStation
m_oViewModel = new BikesAtStationPageViewModel(
model.ActiveUser,
model.Permissions,
CrossBluetoothLE.Current,
App.PermissionsService,
App.BluetoothService,
Device.RuntimePlatform,
model.SelectedStation,
() => model.GetIsConnected(),
(isConnected) => model.GetConnector(isConnected),
model.Geolocation,
App.GeolocationServicesContainer.Active,
model.LocksServices.Active,
model.Polling,
(url) => DependencyService.Get<IExternalBrowserService>().OpenUrl(url),
(d, obj) => synchronizationContext.Post(d, obj),
this);
model.SmartDevice,
this)
{
IsReportLevelVerbose = model.IsReportLevelVerbose
};
}
catch (Exception exception)
{
Log.ForContext<BikesAtStationPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
this.DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
return;
}
@ -130,12 +140,12 @@ namespace TINK.View.BikesAtStation
}
/// <summary> Displays altert message.</summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAlert(string title, string message, string cancel)
{
await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
await App.Current.MainPage.DisplayAlert(title, message, cancel);
}
/// <summary> Displays altert message.</summary>
@ -143,7 +153,7 @@ namespace TINK.View.BikesAtStation
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
@ -153,23 +163,37 @@ namespace TINK.View.BikesAtStation
/// <summary>
/// Displays alert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
{
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
/// <summary> Creates and a page an shows it.</summary>
/// <remarks> When user is not logged in navigation to Login page is supported.</remarks>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
}
public void ShowPage(ViewTypes p_oType, string title = null)
=> m_oNavigation.ShowPage(p_oType.GetViewType(), title);
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
@ -189,6 +213,7 @@ namespace TINK.View.BikesAtStation
throw new NotImplementedException();
}
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Delegate to perform navigation.
/// </summary>
@ -202,6 +227,12 @@ namespace TINK.View.BikesAtStation
set { m_oNavigation = value; }
}
#endif
#if USCSHARP9
public async Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
}
}
#else
public async Task<IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
#endif
}
}

View file

@ -27,21 +27,31 @@ namespace TINK.View.Contact
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotSupportedException();
}
=> throw new NotImplementedException();
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
@ -62,7 +72,10 @@ namespace TINK.View.Contact
{
throw new NotImplementedException();
}
#if USCSHARP9
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#else
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
}
}

View file

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.CommunityToolkit.UI.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FeedbackPopup : Popup<FeedbackPopup.Result>
{
public FeedbackPopup ()
@ -40,7 +35,11 @@ namespace TINK.View
/// <summary>
/// Feedback given by user when returning bike.
/// </summary>
#if USCSHARP9
public class Result : IViewService.IUserFeedback
#else
public class Result : IUserFeedback
#endif
{
/// <summary>
/// Holds whether bike is broken or not.
@ -54,7 +53,6 @@ namespace TINK.View
/// or both.
/// </summary>
public string Message { get; set; }
}
}
}

View file

@ -2,7 +2,6 @@
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TINK.ViewModel.Contact;
using TINK.Model;
namespace TINK.View.Contact
{
@ -16,8 +15,10 @@ namespace TINK.View.Contact
InitializeComponent();
ViewModel = new HelpContactViewModel(
TINK.App.ModelRoot.NextActiveUri.Host,
TINK.App.ModelRoot.IsSiteCachingOn);
App.ModelRoot.NextActiveUri.Host,
App.ModelRoot.IsSiteCachingOn,
resourceName => ViewModelResourceHelper.GetSource(resourceName));
BindingContext = ViewModel;
/// Info about renting.

View file

@ -1,6 +1,8 @@
using System;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel;
using TINK.ViewModel.Info.BikeInfo;
using Xamarin.Forms;
@ -9,9 +11,13 @@ using Xamarin.Forms.Xaml;
namespace TINK.View.Info.BikeInfo
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BikeInfoCarouselPage : CarouselPage, IViewService, IDetailPage
#if USEMASTERDETAIL || USEFLYOUT
public partial class BikeInfoCarouselPage : CarouselPage, IViewService, IDetailPage
#else
public partial class BikeInfoCarouselPage : CarouselPage, IViewService
#endif
{
public BikeInfoCarouselPage ()
public BikeInfoCarouselPage ()
{
InitializeComponent ();
@ -36,13 +42,23 @@ namespace TINK.View.Info.BikeInfo
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
/// <summary>
/// Displays alert message.
/// </summary>
@ -56,14 +72,19 @@ namespace TINK.View.Info.BikeInfo
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
}
=> m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
@ -85,6 +106,7 @@ namespace TINK.View.Info.BikeInfo
throw new NotImplementedException();
}
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Delegate to perform navigation.
/// </summary>
@ -98,6 +120,11 @@ namespace TINK.View.Info.BikeInfo
set { m_oNavigation = value; }
}
#endif
#if USCSHARP9
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#else
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
}
}

View file

@ -1,7 +1,9 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Device;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@ -9,9 +11,13 @@ using Xamarin.Forms.Xaml;
namespace TINK.View.Login
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage, IViewService, IDetailPage
#if USEMASTERDETAIL || USEFLYOUT
public partial class LoginPage : ContentPage, IViewService, IDetailPage
#else
public partial class LoginPage : ContentPage, IViewService
#endif
{
public LoginPage ()
public LoginPage ()
{
InitializeComponent ();
@ -44,25 +50,40 @@ namespace TINK.View.Login
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
}
=> m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
throw new NotSupportedException();
}
@ -80,6 +101,7 @@ namespace TINK.View.Login
await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
}
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Delegate to perform navigation.
/// </summary>
@ -93,6 +115,11 @@ namespace TINK.View.Login
set { m_oNavigation = value; }
}
#endif
#if USCSHARP9
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
}
}
#else
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
}
}

View file

@ -1,21 +1,26 @@
using Plugin.Connectivity;
using System;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Map
{
using TINK.Model.Services.CopriApi.ServerUris;
using Serilog;
using TINK.ViewModel.Map;
using Xamarin.Forms.GoogleMaps;
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
public partial class MapPage : ContentPage, IViewService, IDetailPage
#else
public partial class MapPage : ContentPage, IViewService
#endif
{
/// <summary> View model to notify about whether page appears or hides. </summary>
private MapPageViewModel m_oMapPageViewModel;
private MapPageViewModel MapPageViewModel { get; set; }
/// <summary>
/// Constructs map page instance.
@ -41,13 +46,23 @@ namespace TINK.View.Map
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
/// <summary>
/// Displays alert message.
/// </summary>
@ -61,14 +76,18 @@ namespace TINK.View.Map
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigationMasterDetail.ShowPage(p_oType.GetViewType(), p_strTitle);
}
=> NavigationMasterDetail.ShowPage(p_oType.GetViewType(), p_strTitle);
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Type of page to display.</param>
@ -87,31 +106,34 @@ namespace TINK.View.Map
/// <param name="p_oTypeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes p_oTypeOfPage)
{
#if USEMASTERDETAIL || USEFLYOUT
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType()) as IDetailPage;
#else
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
#endif
if (page == null)
{
return;
}
page.NavigationMasterDetail = m_oNavigationMasterDetail;
#if USEMASTERDETAIL || USEFLYOUT
page.NavigationMasterDetail = NavigationMasterDetail;
#endif
await Navigation.PushAsync((Page)page);
}
#if USCSHARP9
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#else
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
/// <summary> Delegate to perform navigation. </summary>
private INavigationMasterDetail m_oNavigationMasterDetail;
#if USEMASTERDETAIL || USEFLYOUT
/// <summary> Delegate to perform navigation.</summary>
public INavigationMasterDetail NavigationMasterDetail
{
set
{
m_oNavigationMasterDetail = value;
}
}
public INavigationMasterDetail NavigationMasterDetail { private get; set; }
#endif
/// <summary>
/// Invoked when page is shown.
/// Starts update process.
@ -119,39 +141,99 @@ namespace TINK.View.Map
protected async override void OnAppearing()
{
// Pass reference to member Navigation to show bikes at station x dialog.
#if TRYNOTBACKSTYLE
m_oMapPageViewModel = new MapPageViewModel();
#else
m_oMapPageViewModel = new MapPageViewModel(
App.ModelRoot,
(mapspan) => MyMap.MoveToRegion(mapspan),
this,
Navigation);
#endif
BindingContext = m_oMapPageViewModel;
if (Device.RuntimePlatform == Device.iOS)
try
{
TINKButton.BackgroundColor = Color.LightGray;
TINKButton.BorderColor = Color.Black;
TINKButton.Margin = new Thickness(10, 10, 10, 10);
KonradButton.BackgroundColor = Color.LightGray;
KonradButton.BorderColor = Color.Black;
KonradButton.Margin = new Thickness(10, 10, 10, 10);
Log.ForContext<MainPage>().Verbose("Constructing map page view model.");
#if TRYNOTBACKSTYLE
m_oMapPageViewModel = new MapPageViewModel();
#else
MapPageViewModel = new MapPageViewModel(
App.ModelRoot,
App.PermissionsService,
App.BluetoothService,
App.GeolocationServicesContainer.Active,
(mapspan) => MyMap.MoveToRegion(mapspan),
this,
Navigation);
#endif
} catch (Exception exception)
{
Log.ForContext<MainPage>().Error("Constructing map page view model failed. {Exception}", exception);
return;
}
m_oMapPageViewModel.NavigationMasterDetail = m_oNavigationMasterDetail;
try
{
BindingContext = MapPageViewModel;
base.OnAppearing();
#if USEMASTERDETAIL || USEFLYOUT
MapPageViewModel.NavigationMasterDetail = NavigationMasterDetail;
#endif
}
catch (Exception exception)
{
Log.ForContext<MainPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
return;
}
// Pre move and scanle maps to avoid initial display of map in Rome.
MapPageViewModel.MoveAndScale(
(mapSpan) => MyMap.MoveToRegion(mapSpan),
App.ModelRoot.Uris.ActiveUri,
App.ModelRoot.GroupFilterMapPage);
await m_oMapPageViewModel.OnAppearing();
try
{
if (Device.RuntimePlatform == Device.iOS)
{
TINKButton.BackgroundColor = Color.LightGray;
TINKButton.BorderColor = Color.Black;
TINKButton.Margin = new Thickness(10, 10, 10, 10);
KonradButton.BackgroundColor = Color.LightGray;
KonradButton.BorderColor = Color.Black;
KonradButton.Margin = new Thickness(10, 10, 10, 10);
}
}
catch (Exception exception)
{
// Continue because styling is not essential.
Log.ForContext<MainPage>().Error("IOS specific styling of map page failed. {Exception}", exception);
}
try
{
base.OnAppearing();
}
catch (Exception exception)
{
// Continue because styling is not essential.
Log.ForContext<MainPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
return;
}
try
{
// Pre move and scanle maps to avoid initial display of map in Rome.
Log.ForContext<MainPage>().Verbose("Moving and scaling map.");
MapPageViewModel.MoveAndScale(
(mapSpan) => MyMap.MoveToRegion(mapSpan),
App.ModelRoot.Uris.ActiveUri,
App.ModelRoot.GroupFilterMapPage);
}
catch(Exception exception)
{
// Continue because a map not beeing moved/ scaled is no reason for aborting startup.
Log.ForContext<MainPage>().Error("Moving and scaling map failed. {Exception}", exception);
}
try
{
Log.ForContext<MainPage>().Verbose("Invoking OnAppearing on map page view model.");
await MapPageViewModel.OnAppearing();
}
catch (Exception exception)
{
Log.ForContext<MainPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
return;
}
}
/// <summary>
@ -160,10 +242,10 @@ namespace TINK.View.Map
/// </summary>
protected override async void OnDisappearing()
{
if (m_oMapPageViewModel != null)
if (MapPageViewModel != null)
{
// View model might be null.
await m_oMapPageViewModel?.OnDisappearing();
await MapPageViewModel?.OnDisappearing();
}
base.OnDisappearing();

View file

@ -4,7 +4,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Bike.BluetoothLock;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@ -58,21 +60,25 @@ namespace TINK.View.MyBikes
m_oViewModel = new MyBikesPageViewModel(
model.ActiveUser,
model.Permissions,
CrossBluetoothLE.Current,
App.PermissionsService,
App.BluetoothService,
Device.RuntimePlatform,
() => model.GetIsConnected(),
(isConnected) => model.GetConnector(isConnected),
model.Geolocation,
App.GeolocationServicesContainer.Active,
model.LocksServices.Active,
model.Polling,
(d, obj) => synchronizationContext.Post(d, obj),
this);
model.SmartDevice,
this)
{
IsReportLevelVerbose = model.IsReportLevelVerbose
};
}
catch (Exception exception)
{
Log.ForContext<MyBikesPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
this.DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
await DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
return;
}
@ -123,6 +129,16 @@ namespace TINK.View.MyBikes
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
/// <summary>
/// Displays alert message.
/// </summary>
@ -136,14 +152,14 @@ namespace TINK.View.MyBikes
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
#if USEMASTERDETAIL || USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotSupportedException();
}
=> throw new NotImplementedException();
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
@ -165,6 +181,10 @@ namespace TINK.View.MyBikes
throw new NotSupportedException();
}
#if USCSHARP9
public async Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
#else
public async Task<IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
#endif
}
}

View file

@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@ -19,7 +21,11 @@ namespace TINK.View.Root
// - switch to login page form bikes at station page if not yet logged in
/// </remarks>
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
public partial class RootPage : FlyoutPage, INavigationMasterDetail
#else
public partial class RootPage : FlyoutPage
#endif
{
public RootPage()
{
@ -35,6 +41,7 @@ namespace TINK.View.Root
return;
}
#if USEMASTERDETAIL || USEFLYOUT
var detailPage = navigationPage.RootPage as IDetailPage;
if (detailPage == null)
{
@ -42,6 +49,7 @@ namespace TINK.View.Root
}
detailPage.NavigationMasterDetail = this;
#endif
}
/// <summary>
@ -70,12 +78,14 @@ namespace TINK.View.Root
var page = (Page)Activator.CreateInstance(typeOfPage);
page.Title = title;
#if USEMASTERDETAIL || USEFLYOUT
if (page is IDetailPage detailPage)
{
// Detail page needs reference to perform navigation.
// Examples see above in xdoc of class.
detailPage.NavigationMasterDetail = this;
}
#endif
Detail = new NavigationPage(page);
}

View file

@ -8,7 +8,9 @@ using System.Text;
using System.Threading.Tasks;
using TINK.Model;
using TINK.Services;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.View.Themes;
using TINK.ViewModel.Root;
using Xamarin.Forms;

View file

@ -1,6 +1,8 @@
using System;
using TINK.View.Info.BikeInfo;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel.MasterDetail;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@ -13,8 +15,12 @@ namespace TINK.View
public delegate void ShowPageDelegate(Type p_oType, string p_strTitle = null);
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
public partial class MainPage : MasterDetailPage, INavigationMasterDetail
{
#else
public partial class MainPage : MasterDetailPage
#endif
{
public MainPage()
{
InitializeComponent();
@ -29,6 +35,7 @@ namespace TINK.View
return;
}
#if USEMASTERDETAIL || USEFLYOUT
var detailPage = navigationPage.RootPage as IDetailPage;
if (detailPage == null)
{
@ -36,6 +43,7 @@ namespace TINK.View
}
detailPage.NavigationMasterDetail = this;
#endif
}
/// <summary> Creates and a page an shows it.</summary>
@ -50,11 +58,13 @@ namespace TINK.View
var page = (Page)Activator.CreateInstance(p_oTypeOfPage);
page.Title = p_strTitle ?? Helper.GetCaption(p_oTypeOfPage);
#if USEMASTERDETAIL || USEFLYOUT
var l_oPage = page as IDetailPage;
if (l_oPage != null)
{
l_oPage.NavigationMasterDetail = this;
}
#endif
#if !BACKSTYLE
// When bike info page is shown do not allow to close this carousel before all pages were displayed.

View file

@ -20,6 +20,7 @@
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingMapPage}"
Route="MapPage"
ContentTemplate="{DataTemplate mappage:MapPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconMap}" Color="Black" FontFamily="FA-S" />
@ -49,6 +50,7 @@
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingLogin}"
Route="LoginPage"
IsVisible="{Binding IsLoginPageVisible}"
ContentTemplate="{DataTemplate login:LoginPage}">
<ShellContent.FlyoutIcon>

View file

@ -12,6 +12,7 @@
<conv:PermissionToVisibleConverter x:Key="PickLockServiceImplementation_Converter" VisibleFlag="PickLockServiceImplementation"/>
<conv:PermissionToVisibleConverter x:Key="PickLocationServiceImplementation_Converter" VisibleFlag="PickLocationServiceImplementation"/>
<conv:PermissionToVisibleConverter x:Key="PickLoggingLevel_Converter" VisibleFlag="PickLoggingLevel"/>
<conv:PermissionToVisibleConverter x:Key="ReportLevel_Converter" VisibleFlag="ReportLevel"/>
<conv:PermissionToVisibleConverter x:Key="ShowDiagnostics_Converter" VisibleFlag="ShowDiagnostics"/>
<conv:PermissionToVisibleConverter x:Key="SwitchSiteCaching_Converter" VisibleFlag="SwitchNoSiteCaching"/>
</ContentPage.Resources>
@ -176,6 +177,18 @@
IsEnabled="{Binding IsLogToExternalFolderVisible}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- Logging -->
<StackLayout>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ReportLevel_Converter}}"
Text="Verbose error messages" />
<Switch
IsVisible="{Binding DebugLevel, Converter={StaticResource ReportLevel_Converter}}"
IsToggled="{Binding IsReportLevelVerbose}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- Display of parameters -->

View file

@ -2,7 +2,9 @@
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using System;
using TINK.Model.Device;
using Xamarin.CommunityToolkit.Extensions;
@ -10,7 +12,11 @@ using Xamarin.CommunityToolkit.Extensions;
namespace TINK.View.Settings
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SettingsPage : ContentPage, IViewService, IDetailPage
#if USEMASTERDETAIL || USEFLYOUT
public partial class SettingsPage : ContentPage, IViewService, IDetailPage
#else
public partial class SettingsPage : ContentPage, IViewService
#endif
{
/// <summary> Refernce to view model. </summary>
SettingsPageViewModel m_oViewModel = null;
@ -24,6 +30,7 @@ namespace TINK.View.Settings
m_oViewModel = new SettingsPageViewModel(
l_oModel,
App.GeolocationServicesContainer,
this);
BindingContext = m_oViewModel;
@ -43,13 +50,23 @@ namespace TINK.View.Settings
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
/// <summary> Displays alert message.</summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
@ -71,12 +88,18 @@ namespace TINK.View.Settings
public new async Task<string> DisplayActionSheet(String p_strTitle, String p_strCancel, String destruction, params String[] p_oButtons)
=> await base.DisplayActionSheet(p_strTitle, p_strCancel, destruction, p_oButtons);
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
@ -87,6 +110,7 @@ namespace TINK.View.Settings
public Task PopModalAsync()
=> throw new NotSupportedException();
#if USEMASTERDETAIL || USEFLYOUT
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigation;
@ -98,6 +122,7 @@ namespace TINK.View.Settings
set { m_oNavigation = value; }
}
#endif
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
@ -118,7 +143,11 @@ namespace TINK.View.Settings
public async Task PushAsync(ViewTypes p_oTypeOfPage)
=> await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
#if USCSHARP9
public async Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
#else
public async Task<IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
#endif
#if USERFEEDBACKDLG_TRYOUT
public async void OnFeedbackClickedAsync(object sender, EventArgs ev)

View file

@ -28,13 +28,23 @@ namespace TINK.View.WhatsNew.Agb
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
/// <summary> Invoked when page is shown. </summary>
protected async override void OnAppearing()
{
@ -61,11 +71,19 @@ namespace TINK.View.WhatsNew.Agb
throw new NotImplementedException();
}
#if USEMASTERDETAIL || USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotImplementedException();
}
=> throw new NotImplementedException();
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
#if USCSHARP9
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#else
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
}
}

View file

@ -40,6 +40,16 @@ namespace TINK.View.WhatsNew
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays detailed alert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="accept">Text of accept button.</param>
/// <param name="cancel">Text of cancel button.</param>
/// <returns>True if user pressed accept.</returns>
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
public Task PopModalAsync()
{
throw new NotImplementedException(); ;
@ -57,12 +67,20 @@ namespace TINK.View.WhatsNew
await Navigation.PushModalAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
}
#if USEMASTERDETAIL || USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotImplementedException();
}
=> throw new NotImplementedException();
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
#endif
#if USCSHARP9
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#else
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
/// <summary>
/// Invoked when pages is closed/ hidden.

View file

@ -15,7 +15,9 @@ using TINK.View.Root;
using TINK.View.Settings;
using Xamarin.Forms;
using TINK.ViewModel.MasterDetail;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.Services;
using TINK.View.Themes;

View file

@ -87,7 +87,7 @@ namespace TINK.ViewModel.MasterDetail
}
else if (type == typeof(FeesAndBikesPage))
{
return "\uf153";
return "\uf7d9";
}
else if (type == typeof(ContactPage))
{

View file

@ -21,13 +21,13 @@ namespace TINK.Model.Bike.BC
/// <summary> Constructs a bike object.</summary>
protected BikeInfo(
IStateInfo stateInfo,
int id,
string id,
bool? isDemo = DEFAULTVALUEISDEMO,
IEnumerable<string> group = null,
WheelType? wheelType = null,
TypeOfBike? typeOfBike = null,
string description = null,
int? currentStationId = null,
string currentStationId = null,
Uri operatorUri = null,
TariffDescription tariffDescription = null)
{
@ -63,8 +63,8 @@ namespace TINK.Model.Bike.BC
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="wheelType"></param>
public BikeInfo(
int id,
int? currentStationId,
string id,
string currentStationId,
Uri operatorUri = null,
TariffDescription tariffDescription = null,
bool? isDemo = DEFAULTVALUEISDEMO,
@ -99,13 +99,13 @@ namespace TINK.Model.Bike.BC
/// <param name="code">Booking code.</param>
/// <param name="p_oDateTimeNowProvider">Date time provider to calculate reaining time.</param>
public BikeInfo(
int id,
string id,
bool? isDemo,
IEnumerable<string> group,
WheelType? wheelType,
TypeOfBike? typeOfBike,
string description,
int? stationId,
string stationId,
Uri operatorUri,
TariffDescription tariffDescription,
DateTime requestedAt,
@ -142,13 +142,13 @@ namespace TINK.Model.Bike.BC
/// <param name="mailAddress">Mail address of user which booked bike.</param>
/// <param name="code">Booking code.</param>
public BikeInfo(
int id,
string id,
bool? isDemo,
IEnumerable<string> group,
WheelType? wheelType,
TypeOfBike? typeOfBike,
string description,
int? currentStationId,
string currentStationId,
Uri operatorUri,
TariffDescription tariffDescription,
DateTime bookedAt,
@ -179,7 +179,7 @@ namespace TINK.Model.Bike.BC
/// <summary>
/// Station a which bike is located, null otherwise.
/// </summary>
public int? CurrentStation { get; }
public string CurrentStation { get; }
/// <summary> Holds description about the tarif. </summary>
public TariffDescription TariffDescription { get; }
@ -192,7 +192,7 @@ namespace TINK.Model.Bike.BC
get { return m_oStateInfo; }
}
public int Id => Bike.Id;
public string Id => Bike.Id;
public WheelType? WheelType => Bike.WheelType;
@ -210,7 +210,7 @@ namespace TINK.Model.Bike.BC
/// </summary>
public new string ToString()
{
return $"Id={Bike.Id}{(Bike.WheelType != null ? $", wheel(s)={Bike.WheelType}" : string.Empty)}{(Bike.TypeOfBike != null ? $"type={Bike.TypeOfBike}" : "")}, state={State}, location={(CurrentStation.HasValue ? $"Station {CurrentStation}" : "On the road")}, is demo={IsDemo}.";
return $"Id={Bike.Id}{(Bike.WheelType != null ? $", wheel(s)={Bike.WheelType}" : string.Empty)}{(Bike.TypeOfBike != null ? $"type={Bike.TypeOfBike}" : "")}, state={State}, location={(!string.IsNullOrEmpty(CurrentStation)? $"Station {CurrentStation}" : "On the road")}, is demo={IsDemo}.";
}
}
}

View file

@ -29,13 +29,13 @@ namespace TINK.Model.Bike.BC
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="stateInfo">Bike state info.</param>
protected BikeInfoMutable(
int id,
string id,
bool isDemo = BikeInfo.DEFAULTVALUEISDEMO,
IEnumerable<string> group = null,
WheelType? wheelType = null,
TypeOfBike? typeOfBike = null,
string description = null,
int? currentStationId = null,
string currentStationId = null,
Uri operatorUri = null,
TariffDescription tariffDescription = null,
Func<DateTime> dateTimeProvider = null,
@ -71,7 +71,7 @@ namespace TINK.Model.Bike.BC
/// Station a which bike is located, null otherwise.
/// </summary>
[DataMember]
public int? CurrentStation { get; }
public string CurrentStation { get; }
/// <summary> Holds description about the tarif. </summary>
[DataMember]
@ -94,7 +94,7 @@ namespace TINK.Model.Bike.BC
/// <summary> Unused member. </summary>
IStateInfoMutable IBikeInfoMutable.State => m_oStateInfo;
public int Id => m_oBike.Id;
public string Id => m_oBike.Id;
public bool IsDemo { get; }
@ -118,7 +118,7 @@ namespace TINK.Model.Bike.BC
/// <returns></returns>
public new string ToString()
{
return $"Id={Id}{(WheelType != null ? $", wheel(s)={WheelType}" : string.Empty)}{(TypeOfBike != null ? $", type={TypeOfBike}" : "")}, demo={IsDemo}, state={State.ToString()}, location={(CurrentStation.HasValue ? $"Station {CurrentStation}" : "On the road")}.";
return $"Id={Id}{(WheelType != null ? $", wheel(s)={WheelType}" : string.Empty)}{(TypeOfBike != null ? $", type={TypeOfBike}" : "")}, demo={IsDemo}, state={State.ToString()}, location={(!string.IsNullOrEmpty(CurrentStation) ? $"Station {CurrentStation}" : "On the road")}.";
}
}
}

View file

@ -13,7 +13,7 @@ namespace TINK.Model.Bike.BC
/// <summary>
/// Holds the unique id of the bike;
/// </summary>
int Id { get; }
string Id { get; }
/// <summary> True if bike is a demo bike. </summary>
bool IsDemo { get; }
@ -37,7 +37,7 @@ namespace TINK.Model.Bike.BC
/// <summary>
/// Station a which bike is located, null otherwise.
/// </summary>
int? CurrentStation { get; }
string CurrentStation { get; }
/// <summary>
/// Uri of the operator or null, in case of single operator setup.

View file

@ -11,7 +11,7 @@ namespace TINK.Model.Bikes.Bike.BC
/// <summary>
/// Holds the unique id of the bike;
/// </summary>
int Id { get; }
string Id { get; }
/// <summary> True if bike is a demo bike. </summary>
bool IsDemo { get; }
@ -35,7 +35,7 @@ namespace TINK.Model.Bikes.Bike.BC
/// <summary>
/// Station a which bike is located, null otherwise.
/// </summary>
int? CurrentStation { get; }
string CurrentStation { get; }
/// <summary>
/// Holds the rent state of the bike.

View file

@ -29,7 +29,7 @@ namespace TINK.Model.Bike
/// <param name="p_iId">Unique id of bike.</param>
/// <param name="p_strCurrentStationName">Name of station where bike is located, null if bike is on the road.</param>
public Bike(
int p_iId,
string p_iId,
WheelType? wheelType = null,
TypeOfBike? typeOfBike = null,
string description = null)
@ -43,7 +43,7 @@ namespace TINK.Model.Bike
/// <summary>
/// Holds the unique id of the bike;
/// </summary>
public int Id { get; }
public string Id { get; }
/// <summary>
/// Holds the count of wheels.

View file

@ -18,10 +18,10 @@ namespace TINK.Model.Bike.BluetoothLock
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="wheelType">Trike, two wheels, mono, ....</param>
public BikeInfo(
int bikeId,
string bikeId,
int lockId,
Guid lockGuid,
int? currentStationId,
string currentStationId,
Uri operatorUri = null,
TariffDescription tariffDescription = null,
bool? isDemo = DEFAULTVALUEISDEMO,
@ -58,7 +58,7 @@ namespace TINK.Model.Bike.BluetoothLock
/// <param name="p_oDateTimeNowProvider">Date time provider to calculate reaining time.</param>
/// <param name="wheelType"></param>
public BikeInfo(
int id,
string id,
int lockId,
Guid lockGuid,
byte[] userKey,
@ -66,7 +66,7 @@ namespace TINK.Model.Bike.BluetoothLock
byte[] seed,
DateTime requestedAt,
string mailAddress,
int? currentStationId,
string currentStationId,
Uri operatorUri,
TariffDescription tariffDescription,
Func<DateTime> dateTimeProvider,
@ -106,7 +106,7 @@ namespace TINK.Model.Bike.BluetoothLock
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="wheelType"></param>
public BikeInfo(
int id,
string id,
int lockId,
Guid lockGuid,
byte[] userKey,
@ -114,7 +114,7 @@ namespace TINK.Model.Bike.BluetoothLock
byte[] seed,
DateTime bookedAt,
string mailAddress,
int? currentStationId,
string currentStationId,
Uri operatorUri,
TariffDescription tariffDescription = null,
bool? isDemo = DEFAULTVALUEISDEMO,

View file

@ -5,8 +5,9 @@ namespace TINK.Model.Bikes.Bike
/// <summary>
/// Holds tariff info for a single bike.
/// </summary>
#if USCSHARP9
public record TariffDescription
{
{
/// <summary>
/// Name of the tariff.
/// </summary>
@ -37,4 +38,38 @@ namespace TINK.Model.Bikes.Bike
/// </summary>
public double MaxFeeEuroPerDay { get; init; }
}
#else
public class TariffDescription
{
/// <summary>
/// Name of the tariff.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Number of the tariff.
/// </summary>
public int? Number { get; set; }
/// <summary>
/// Costs per hour in euro.
/// </summary>
public double FeeEuroPerHour { get; set; }
/// <summary>
/// Costs of the abo per month.
/// </summary>
public double AboEuroPerMonth { get; set; }
/// <summary>
/// Costs per hour in euro.
/// </summary>
public TimeSpan FreeTimePerSession { get; set; }
/// <summary>
/// Max. costs per day in euro.
/// </summary>
public double MaxFeeEuroPerDay { get; set; }
}
#endif
}

View file

@ -11,37 +11,37 @@ namespace TINK.Model.Bike
public class BikeCollection : IBikeDictionary<BikeInfo>
{
/// <summary> Holds the bike dictionary object.</summary>
private Dictionary<int, BikeInfo> BikeDictionary { get; }
private Dictionary<string, BikeInfo> BikeDictionary { get; }
/// <summary>Constructs an empty bike info dictionary object.</summary>
public BikeCollection()
{
BikeDictionary = new Dictionary<int, BikeInfo>();
BikeDictionary = new Dictionary<string, BikeInfo>();
}
/// <summary> Constructs a bike collection object.</summary>
/// <param name="bikeDictionary"></param>
public BikeCollection(Dictionary<int, BikeInfo> bikeDictionary)
public BikeCollection(Dictionary<string, BikeInfo> bikeDictionary)
{
BikeDictionary = bikeDictionary ??
throw new ArgumentNullException(nameof(bikeDictionary), "Can not construct BikeCollection object.");
}
/// <summary> Gets a bike by its id.</summary>
/// <param name="p_iId">Id of the bike to get.</param>
/// <param name="id">Id of the bike to get.</param>
/// <returns></returns>
public BikeInfo GetById(int p_iId)
public BikeInfo GetById(string id)
{
return BikeDictionary.FirstOrDefault(x => x.Key == p_iId).Value;
return BikeDictionary.FirstOrDefault(x => x.Key == id).Value;
}
/// <summary> Gets the count of bikes. </summary>
public int Count => BikeDictionary.Count;
/// <summary> Gets if a bike with given id exists.</summary>
/// <param name="p_iId">Id of bike.</param>
/// <param name="id">Id of bike.</param>
/// <returns>True if bike is contained, false otherwise.</returns>
public bool ContainsKey(int p_iId) => BikeDictionary.Keys.Contains(p_iId);
public bool ContainsKey(string id) => BikeDictionary.Keys.Contains(id);
/// <summary> Gets the enumerator. </summary>
/// <returns>Enumerator object.</returns>

View file

@ -14,11 +14,11 @@ namespace TINK.Model
/// <returns>BikeCollection holding bikes at given station or empty BikeCollection, if there are no bikes.</returns>
public static BikeCollection GetAtStation(
this BikeCollection bikesAtAnyStation,
int? selectedStation)
string selectedStation)
{
return new BikeCollection(bikesAtAnyStation?
.Where(bike => selectedStation.HasValue && bike.CurrentStation == selectedStation.Value)
.ToDictionary(x => x.Id) ?? new Dictionary<int, BikeInfo>());
.Where(bike => !string.IsNullOrEmpty(selectedStation) && bike.CurrentStation == selectedStation)
.ToDictionary(x => x.Id) ?? new Dictionary<string, BikeInfo>());
}
/// <summary> Filters bikes by bike type. </summary>
@ -28,7 +28,7 @@ namespace TINK.Model
{
return new BikeCollection(bcAndLockItBikes?
.Where(bike => bike is Bike.BluetoothLock.BikeInfo)
.ToDictionary(x => x.Id) ?? new Dictionary<int, BikeInfo>());
.ToDictionary(x => x.Id) ?? new Dictionary<string, BikeInfo>());
}
}
}

View file

@ -44,7 +44,7 @@ namespace TINK.Model.Bike
// Update bike.
GetById(bikeInfo.Id).State.Load(bikeInfo.State);
if (bikesToBeRemoved.Contains<int>(bikeInfo.Id))
if (bikesToBeRemoved.Contains<string>(bikeInfo.Id))
{
// Remove list from obsolete list.
bikesToBeRemoved.Remove(bikeInfo.Id);
@ -86,20 +86,20 @@ namespace TINK.Model.Bike
private set;
}
public void SetSelectedBike(int p_intId)
public void SetSelectedBike(string id)
{
SelectedBike = GetById(p_intId);
SelectedBike = GetById(id);
}
/// <summary>
/// Gets a bike by its id.
/// </summary>
/// <param name="p_iId"></param>
/// <param name="id"></param>
/// <returns></returns>
public BikeInfoMutable GetById(int p_iId)
public BikeInfoMutable GetById(string id)
{
{
return this.FirstOrDefault(bike => bike.Id == p_iId);
return this.FirstOrDefault(bike => bike.Id == id);
}
}
@ -108,18 +108,18 @@ namespace TINK.Model.Bike
/// </summary>
/// <param name="p_strKey">Key to check.</param>
/// <returns>True if bike exists.</returns>
public bool ContainsKey(int p_iId)
public bool ContainsKey(string id)
{
return GetById(p_iId) != null;
return GetById(id) != null;
}
/// <summary>
/// Removes a bike by its id.
/// </summary>
/// <param name="p_iId">Id of bike to be removed.</param>
public void RemoveById(int p_iId)
/// <param name="id">Id of bike to be removed.</param>
public void RemoveById(string id)
{
var l_oBike = GetById(p_iId);
var l_oBike = GetById(id);
if (l_oBike == null)
{
// Nothing to do if bike does not exists.

View file

@ -14,7 +14,7 @@ namespace TINK.Model.Bike
IEnumerable<BluetoothLock.LockInfo> locksInfo)
{
var updatedBikesCollection = new Dictionary<int, BC.BikeInfo>();
var updatedBikesCollection = new Dictionary<string, BC.BikeInfo>();
foreach (var bikeInfo in bikes)
{

View file

@ -7,29 +7,29 @@ namespace TINK.Model.Bike
/// <summary>
/// Gets a bike by its id.
/// </summary>
/// <param name="p_iId"></param>
/// <param name="id"></param>
/// <returns></returns>
T GetById(int p_iId);
T GetById(string id);
/// <summary>
/// Deteermines whether a bike by given key exists.
/// </summary>
/// <param name="p_strKey">Key to check.</param>
/// <returns>True if bike exists.</returns>
bool ContainsKey(int p_iId);
bool ContainsKey(string id);
}
public interface IBikeDictionaryMutable<T> : IBikeDictionary<T>
{
/// <summary>
/// Removes a bike by its id.
/// </summary>
/// <param name="p_iId">Id of bike to be removed.</param>
void RemoveById(int p_iId);
/// <param name="id">Id of bike to be removed.</param>
void RemoveById(string id);
/// <summary>
/// Adds a new element to dictinary.
/// </summary>
/// <param name="p_oNewElement">New element to add.</param>
void Add(T p_oNewElement);
/// <param name="newElement">New element to add.</param>
void Add(T newElement);
}
}

View file

@ -1,10 +1,11 @@
using Serilog;
using System;
using System.Threading.Tasks;
using TINK.Model.Repository;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Repository;
using TINK.Repository.Request;
using TINK.Repository.Response;
using TINK.Model.User.Account;
using TINK.Model.Device;
namespace TINK.Model.Connector
{
@ -122,7 +123,8 @@ namespace TINK.Model.Connector
public async Task DoReturn(
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
LocationDto location)
LocationDto location,
ISmartDevice smartDevice)
{
Log.ForContext<Command>().Error("Unexpected returning request detected. No user logged in.");
await Task.CompletedTask;
@ -132,7 +134,11 @@ namespace TINK.Model.Connector
/// Submits feedback to copri server.
/// </summary>
/// <param name="userFeedback">Feedback to submit.</param>
#if USCSHARP9
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
#else
public async Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri)
#endif
{
Log.ForContext<Command>().Error("Unexpected submit feedback request detected. No user logged in.");
await Task.CompletedTask;

View file

@ -1,11 +1,12 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Bike.BluetoothLock;
using TINK.Model.Repository;
using TINK.Model.Repository.Exception;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Repository;
using TINK.Repository.Exception;
using TINK.Repository.Request;
using TINK.Repository.Response;
using TINK.Model.User.Account;
using TINK.Model.Device;
namespace TINK.Model.Connector
{
@ -30,7 +31,7 @@ namespace TINK.Model.Connector
/// Logs user in.
/// If log in succeeds either and session might be updated if it was no more valid (logged in by an different device).
/// If log in fails (password modified) session cookie is set to empty.
/// If communication fails an TINK.Model.Repository.Exception is thrown.
/// If communication fails an TINK.Repository.Exception is thrown.
/// </summary>
/// <param name="p_oAccount">Account to use for login.</param>
public Task<IAccount> DoLogin(string p_strMail, string p_strPassword, string p_strDeviceId)
@ -242,12 +243,13 @@ namespace TINK.Model.Connector
}
/// <summary> Request to return a bike.</summary>
/// <param name="latitude">Latitude of the bike.</param>
/// <param name="longitude">Longitude of the bike.</param>
/// <param name="bike">Bike to return.</param>
/// <param name="locaton">Position of the bike.</param>
/// <param name="smartDevice">Provides info about hard and software.</param>
public async Task DoReturn(
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
LocationDto location)
LocationDto location,
ISmartDevice smartDevice)
{
if (bike == null)
{
@ -257,7 +259,7 @@ namespace TINK.Model.Connector
ReservationCancelReturnResponse l_oResponse;
try
{
l_oResponse = (await CopriServer.DoReturn(bike.Id, location, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
l_oResponse = (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
}
catch (Exception)
{
@ -272,8 +274,12 @@ namespace TINK.Model.Connector
/// Submits feedback to copri server.
/// </summary>
/// <param name="userFeedback">Feedback to submit.</param>
#if USCSHARP9
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
=> await CopriServer.DoSubmitFeedback(userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
#else
public async Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri)
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
#endif
}
}

View file

@ -1,7 +1,8 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Repository.Request;
using TINK.Repository.Request;
using TINK.Model.User.Account;
using TINK.Model.Device;
namespace TINK.Model.Connector
{
@ -44,9 +45,10 @@ namespace TINK.Model.Connector
Task DoBook(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike);
/// <summary> Request to return a bike.</summary>
/// <param name="location">Geolocation of lock when returning bike.</param>
/// <param name="bike">Bike to return.</param>
Task DoReturn(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto geolocation = null);
/// <param name="location">Geolocation of lock when returning bike.</param>
/// <param name="smartDevice">Provides info about hard and software.</param>
Task DoReturn(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto geolocation = null, ISmartDevice smartDevice = null);
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
bool IsConnected { get; }
@ -55,12 +57,15 @@ namespace TINK.Model.Connector
string SessionCookie { get; }
Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri);
#if USCSHARP9
/// <summary>
/// Feedback given by user when returning bike.
/// </summary>
public interface IUserFeedback
{
/// <summary> Id of the bike to which the feedback is related to.</summary>
string BikeId { get; }
/// <summary>
/// Holds whether bike is broken or not.
/// </summary>
@ -74,12 +79,37 @@ namespace TINK.Model.Connector
/// </summary>
string Message { get; }
}
#endif
}
/// <summary>Defines delegate to be raised whenever login state changes.</summary>
/// <param name="p_oEventArgs">Holds session cookie and mail address if user logged in successfully.</param>
public delegate void LoginStateChangedEventHandler(object p_oSender, LoginStateChangedEventArgs p_oEventArgs);
#if !USCSHARP9
/// <summary>
/// Feedback given by user when returning bike.
/// </summary>
public interface IUserFeedback
{
/// <summary> Id of the bike to which the feedback is related to.</summary>
string BikeId { get; }
/// <summary>
/// Holds whether bike is broken or not.
/// </summary>
bool IsBikeBroken { get; }
/// <summary>
/// Holds either
/// - general feedback
/// - error description of broken bike
/// or both.
/// </summary>
string Message { get; }
}
#endif
/// <summary> Event arguments to notify about changes of logged in state.</summary>
public class LoginStateChangedEventArgs : EventArgs
{

Some files were not shown because too many files have changed in this diff Show more