From 9c6a1fa92bb8a597adf271847b902d8ba9950599 Mon Sep 17 00:00:00 2001 From: Oliver Hauff Date: Sat, 26 Jun 2021 20:57:55 +0200 Subject: [PATCH] Code updated to 3.0.238 --- LockItBLE/LockItBLE.csproj | 4 +- .../BluetoothLock/BLE/LockItEventBased.cs | 12 +- .../BluetoothLock/BLE/LockItPolling.cs | 16 +- LockItShared/LockItShared.csproj | 2 +- .../MultilingualResources/LockItShared.de.xlf | 9 +- .../Resources.Designer.cs | 18 +- .../MultilingualResources/Resources.de.resx | 3 - .../MultilingualResources/Resources.resx | 6 +- .../CouldntOpenBoldBlockedException.cs | 13 - .../CouldntOpenBoldIsBlockedException.cs | 16 + .../CouldntOpenBoldWasBlockedException.cs | 16 + .../CouldntOpenInconsistentStateExecption.cs | 4 +- Sharee.sln => TINK.sln | 76 +- TINK/TINK.Android/Model/Device/Device.cs | 17 +- .../Properties/AndroidManifest.xml | 4 +- .../Properties/AndroidManifest.xml.bak | 20 + .../Resources/Resource.Designer.cs | 1785 +++++++++-------- ...ree.Android.csproj => TINK.Android.csproj} | 24 +- TINK/TINK.UWP/App.xaml | 8 - TINK/TINK.UWP/App.xaml.cs | 107 - .../Assets/LockScreenLogo.scale-100.png | Bin 261 -> 0 bytes .../Assets/LockScreenLogo.scale-125.png | Bin 305 -> 0 bytes .../Assets/LockScreenLogo.scale-150.png | Bin 347 -> 0 bytes .../Assets/LockScreenLogo.scale-200.png | Bin 431 -> 0 bytes .../Assets/LockScreenLogo.scale-400.png | Bin 758 -> 0 bytes .../Assets/SplashScreen.scale-100.png | Bin 1706 -> 0 bytes .../Assets/SplashScreen.scale-125.png | Bin 2148 -> 0 bytes .../Assets/SplashScreen.scale-150.png | Bin 2609 -> 0 bytes .../Assets/SplashScreen.scale-200.png | Bin 3566 -> 0 bytes .../Assets/SplashScreen.scale-400.png | Bin 8784 -> 0 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 1948 -> 0 bytes .../Assets/Square44x44Logo.scale-100.png | Bin 394 -> 0 bytes .../Assets/Square44x44Logo.scale-125.png | Bin 483 -> 0 bytes .../Assets/Square44x44Logo.scale-150.png | Bin 563 -> 0 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 658 -> 0 bytes .../Assets/Square44x44Logo.scale-400.png | Bin 1152 -> 0 bytes ...x44Logo.targetsize-16_altform-unplated.png | Bin 196 -> 0 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 253 -> 0 bytes ...44Logo.targetsize-256_altform-unplated.png | Bin 1656 -> 0 bytes ...x44Logo.targetsize-32_altform-unplated.png | Bin 314 -> 0 bytes ...x44Logo.targetsize-48_altform-unplated.png | Bin 422 -> 0 bytes TINK/TINK.UWP/Assets/StoreLogo.png | Bin 392 -> 0 bytes .../Assets/Wide310x150Logo.scale-100.png | Bin 921 -> 0 bytes .../Assets/Wide310x150Logo.scale-125.png | Bin 1149 -> 0 bytes .../Assets/Wide310x150Logo.scale-150.png | Bin 1369 -> 0 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 1719 -> 0 bytes .../Assets/Wide310x150Logo.scale-400.png | Bin 3682 -> 0 bytes TINK/TINK.UWP/Assets/tink2.png | Bin 3765 -> 0 bytes TINK/TINK.UWP/Device/WinPhoneDevice .cs | 32 - TINK/TINK.UWP/MainPage.xaml | 15 - TINK/TINK.UWP/MainPage.xaml.cs | 19 - TINK/TINK.UWP/Package.appxmanifest | 49 - TINK/TINK.UWP/Properties/AssemblyInfo.cs | 29 - TINK/TINK.UWP/Properties/Default.rd.xml | 31 - TINK/TINK.UWP/TINK.UWP.csproj | 171 -- TINK/TINK.UWP/project.json | 28 - TINK/TINK.iOS/Device/Device.cs | 16 +- TINK/TINK.iOS/Entitlements.plist | 7 +- TINK/TINK.iOS/Info.plist | 4 +- .../{Sharee.iOS.csproj => TINK.iOS.csproj} | 16 +- TINK/TINK/App.xaml.cs | 176 +- TINK/TINK/BackdoorMethodHelpers.cs | 8 +- TINK/TINK/Model/User/Account/Store.cs | 60 +- TINK/TINK/TINK.projitems | 2 +- TINK/TINK/{Sharee.shproj => TINK.shproj} | 0 TINK/TINK/View/Account/AccountPage.xaml.cs | 67 +- TINK/TINK/View/Bike/ILockItBike.xaml | 7 + .../BikesAtStation/BikesAtStationPage.xaml.cs | 83 +- TINK/TINK/View/Contact/ContactPage.xaml.cs | 31 +- TINK/TINK/View/FeedbackPopup.xaml.cs | 12 +- .../FeesAndBikes/FeesAndBikesPage.xaml.cs | 7 +- .../BikeInfo/BikeInfoCarouselPage.xaml.cs | 39 +- TINK/TINK/View/Login/LoginPage.xaml.cs | 45 +- TINK/TINK/View/Map/MapPage.xaml.cs | 178 +- TINK/TINK/View/MyBikes/MyBikesPage.xaml.cs | 44 +- TINK/TINK/View/RootFlyout/RootPage.xaml.cs | 10 + .../View/RootFlyout/RootPageFlyout.xaml.cs | 2 + .../View/RootMasterDetail/MainPage.xaml.cs | 12 +- TINK/TINK/View/RootShell/AppShell.xaml | 2 + TINK/TINK/View/Settings/SettingsPage.xaml | 13 + TINK/TINK/View/Settings/SettingsPage.xaml.cs | 33 +- TINK/TINK/View/WhatsNew/Agb/AgbPage.xaml.cs | 26 +- TINK/TINK/View/WhatsNew/WhatsNewPage.xaml.cs | 24 +- .../ViewModel/RootFlyout/RootPageViewModel.cs | 2 + .../TINK/ViewModel/RootMasterDetail/Helper.cs | 2 +- TINKLib/Model/Bikes/Bike/BC/BikeInfo.cs | 22 +- .../Model/Bikes/Bike/BC/BikeInfoMutable.cs | 10 +- TINKLib/Model/Bikes/Bike/BC/IBikeInfo.cs | 4 +- .../Model/Bikes/Bike/BC/IBikeInfoMutable.cs | 4 +- TINKLib/Model/Bikes/Bike/Bike.cs | 4 +- .../Bikes/Bike/BluetoothLock/BikeInfo.cs | 12 +- TINKLib/Model/Bikes/Bike/TariffDescription.cs | 37 +- TINKLib/Model/Bikes/BikeCollection.cs | 16 +- TINKLib/Model/Bikes/BikeCollectionFilter.cs | 8 +- TINKLib/Model/Bikes/BikeCollectionMutable.cs | 22 +- TINKLib/Model/Bikes/BikeCollectionUpdater.cs | 2 +- TINKLib/Model/Bikes/IBikeCollection.cs | 14 +- TINKLib/Model/Connector/Command/Command.cs | 14 +- .../Connector/Command/CommandLoggedIn.cs | 28 +- TINKLib/Model/Connector/Command/ICommand.cs | 38 +- .../Connector/Command/UserFeedbackDto.cs | 16 + TINKLib/Model/Connector/Connector.cs | 2 +- TINKLib/Model/Connector/ConnectorCache.cs | 2 +- .../Connector/Filter/GroupFilterFactory.cs | 15 +- TINKLib/Model/Connector/FilteredConnector.cs | 28 +- .../Model/Connector/NullFilterConnector.cs | 10 +- TINKLib/Model/Connector/Query/Base.cs | 2 +- TINKLib/Model/Connector/Query/BaseLoggedIn.cs | 2 +- TINKLib/Model/Connector/Query/CachedQuery.cs | 4 +- .../Connector/Query/CachedQueryLoggedIn.cs | 66 +- TINKLib/Model/Connector/Query/Query.cs | 4 +- .../Model/Connector/Query/QueryLoggedIn.cs | 2 +- TINKLib/Model/Connector/TextToTypeHelper.cs | 41 +- .../Model/Connector/Updater/UpdaterJSON.cs | 14 +- TINKLib/Model/Device/ISmartDevice.cs | 21 + TINKLib/Model/ITinkApp.cs | 23 +- .../Model/Logging/LogEntryClassifyHelper.cs | 2 +- .../Logging/LoggerConfigurationHelper.cs | 40 +- .../Model/Logging/LoggingDirectoryManager.cs | 3 +- .../Model/Settings/JsonSettingsDictionary.cs | 13 + TINKLib/Model/Settings/Settings.cs | 8 + TINKLib/Model/Station/IStation.cs | 2 +- TINKLib/Model/Station/NullStation.cs | 2 +- TINKLib/Model/Station/Station.cs | 4 +- TINKLib/Model/Station/StationCollection.cs | 32 +- TINKLib/Model/TinkApp.cs | 62 +- TINKLib/Model/User/Account/Account.cs | 30 +- TINKLib/Model/User/Account/IStore.cs | 16 +- TINKLib/Model/User/Account/Store.cs | 67 + TINKLib/Model/User/User.cs | 17 +- TINKLib/Model/WhatsNew.cs | 57 +- .../AppResources.Designer.cs | 207 +- .../AppResources.de.resx | 90 +- .../MultilingualResources/AppResources.resx | 83 +- TINKLib/MultilingualResources/TINKLib.de.xlf | 125 +- TINKLib/Repository/CopriCallsHttps.cs | 104 +- TINKLib/Repository/CopriCallsMemory.cs | 1314 ++++++++++++ TINKLib/Repository/CopriCallsMonkeyStore.cs | 105 +- TINKLib/Repository/CopriCallsStatic.cs | 69 +- .../AuthcookieNotDefinedException.cs | 16 +- .../AuthorizationResponseException.cs | 2 +- .../Exception/BookingDeclinedException.cs | 2 +- .../Exception/CallNotRequiredException.cs | 2 +- .../Exception/CommunicationException.cs | 2 +- .../Exception/DeserializationException.cs | 4 +- .../Exception/InvalidResponseException.cs | 2 +- .../Exception/NoGPSDataException.cs | 3 +- .../Exception/NotAtStationException.cs | 2 +- .../Repository/Exception/ResponseException.cs | 2 +- .../Exception/ReturnBikeException.cs | 2 +- ...nsupportedCopriVersionDetectedException.cs | 8 + .../Exception/WebConnectFailureException.cs | 2 +- .../Exception/WebExceptionHelper.cs | 39 +- .../Exception/WebForbiddenException.cs | 2 +- TINKLib/Repository/ICopriServer.cs | 22 +- TINKLib/Repository/Request/IRequestBuilder.cs | 19 +- TINKLib/Repository/Request/RequestBuilder.cs | 21 +- .../Request/RequestBuilderLoggedIn.cs | 56 +- .../Response/AuthorizationResponse.cs | 4 +- .../Response/AuthorizationoutResponse.cs | 2 +- .../Repository/Response/BikeInfoAvailable.cs | 4 +- TINKLib/Repository/Response/BikeInfoBase.cs | 9 +- .../Response/BikeInfoReservedBooked.cs | 2 +- .../Response/BikesAvailableResponse.cs | 4 +- .../Response/BikesReservedOccupiedResponse.cs | 4 +- TINKLib/Repository/Response/CopriVersion.cs | 11 + TINKLib/Repository/Response/GpsInfo.cs | 23 + .../{JsonConvert.cs => JsonConvertRethrow.cs} | 2 +- .../Response/ReservationBookingResponse.cs | 2 +- .../ReservationCancelReturnResponse.cs | 2 +- TINKLib/Repository/Response/ResponseBase.cs | 7 +- .../Repository/Response/ResponseContainer.cs | 8 +- TINKLib/Repository/Response/ResponseHelper.cs | 33 +- .../Response/StationsAllResponse.cs | 10 +- .../Response/SubmitFeedbackResponse.cs | 4 +- .../Repository/Response/TariffDescription.cs | 4 + .../Response/VersionindependentResponse.cs | 22 + .../Services/CopriApi/CopriProviderHttps.cs | 38 +- .../CopriApi/CopriProviderMonkeyStore.cs | 26 +- .../Services/CopriApi/ICachedCopriServer.cs | 5 +- TINKLib/Services/CopriApi/ICopriCache.cs | 4 +- .../CopriApi/ServerUris/CopriHelper.cs | 6 +- .../CopriApi/ServerUris/CopriServerUriList.cs | 6 +- TINKLib/Services/IServicesContainer.cs | 14 + TINKLib/Services/ServicesContainerMutable.cs | 2 +- TINKLib/TINKLib.csproj | 21 +- TINKLib/View/IViewService.cs | 56 +- .../EmptyNavigationMasterDetail.cs | 5 +- TINKLib/View/MasterDetail/IDetailPage.cs | 4 +- .../MasterDetail/INavigationMasterDetail.cs | 4 +- .../ViewModel/Account/AccountPageViewModel.cs | 20 +- .../ViewModel/Bikes/Bike/BC/BikeViewModel.cs | 9 +- .../Bikes/Bike/BC/RequestHandler/Base.cs | 7 + .../Bike/BC/RequestHandler/Disposable.cs | 13 +- .../Bike/BC/RequestHandler/NotLoggedIn.cs | 7 +- .../Bikes/Bike/BC/RequestHandler/Reserved.cs | 11 +- .../Bikes/Bike/BC/RequestHandlerFactory.cs | 5 + .../ViewModel/Bikes/Bike/BikeViewModelBase.cs | 36 +- .../Bikes/Bike/BikeViewModelFactory.cs | 7 +- .../Bikes/Bike/BluetoothLock/BikeViewModel.cs | 9 +- .../Bike/BluetoothLock/RequestHandler/Base.cs | 5 +- .../RequestHandler/BookedClosed.cs | 59 +- .../RequestHandler/BookedDisconnected.cs | 34 +- .../RequestHandler/BookedOpen.cs | 49 +- .../RequestHandler/BookedUnknown.cs | 43 +- .../RequestHandler/DisposableDisconnected.cs | 54 +- .../RequestHandler/DisposableOpen.cs | 33 +- .../RequestHandler/InvalidState.cs | 4 +- .../RequestHandler/NotLoggedIn.cs | 5 +- .../RequestHandler/ReservedClosed.cs | 50 +- .../RequestHandler/ReservedDisconnected.cs | 64 +- .../RequestHandler/ReservedOpen.cs | 35 +- .../RequestHandler/ReservedUnknown.cs | 32 +- .../BluetoothLock/RequestHandlerFactory.cs | 13 + .../Bikes/Bike/TariffDescriptionViewModel.cs | 6 +- TINKLib/ViewModel/Bikes/BikesViewModel.cs | 54 +- .../BikeAtStationInUseStateInfoProvider.cs | 4 +- .../BikesAtStationPageViewModel.cs | 70 +- .../FeesAndBikes/HelpContactViewModel.cs | 20 +- TINKLib/ViewModel/IInUseStateInfoProvider.cs | 4 +- .../BikeInfo/BikeInfoCarouselViewModel.cs | 14 +- TINKLib/ViewModel/Login/LoginPageViewModel.cs | 30 +- TINKLib/ViewModel/Map/MapPageViewModel.cs | 231 ++- TINKLib/ViewModel/MyBikes/MyBikeViewModel.cs | 14 +- .../ViewModel/MyBikes/MyBikesPageViewModel.cs | 16 +- TINKLib/ViewModel/PollingUpdateTask.cs | 3 +- .../Settings/SettingsPageViewModel.cs | 24 +- TINKLib/ViewModel/ViewModelHelper.cs | 68 +- .../ViewModel/WhatsNew/WhatsNewViewModel.cs | 7 +- .../BluetoothLock/BLE/TestLockItEventBased.cs | 2 +- .../BluetoothLock/BLE/TestLockItPolling.cs | 2 +- TestLockItBLE/TestLockItBLE.csproj | 6 +- ...stCouldntOpenInconsistentStateExecption.cs | 2 +- TestLockItShared/TestLockItShared.csproj | 6 +- .../Model/Bike/BC/TestBikeMutable.cs | 20 +- .../Model/Bike/BluetoothLock/TestBikeInfo.cs | 20 +- TestShareeLib/Model/Bike/TestBike.cs | 20 +- .../Model/Bike/TestBikeCollection.cs | 2 +- .../Model/Bike/TestBikeCollectionMutable.cs | 58 +- .../Model/Connector/TestCachetimings.cs | 160 ++ .../Model/Connector/TestConnectorFactory.cs | 30 + .../Model/Connector/TestCopriProviderHttps.cs | 514 +++++ .../Model/Connector/TestUpdaterJSON.cs | 866 ++++++++ .../Model/TestBikeCollectionFilter.cs | 30 +- TestShareeLib/Model/TestTinkApp.cs | 74 + TestShareeLib/Model/TestTinkAppLogin.cs | 67 + .../Response/TestBikesAvailableResponse.cs | 20 +- .../Repository/Response/TestJsonConvert.cs | 2 +- .../Response/TestTariffDescription.cs | 6 +- .../Repository/TestCopriCallsHttps.cs | 17 + .../Repository/TestCopriCallsMonkeyStore.cs | 83 + .../Repository/TestCopriCallsStatic.cs | 86 + TestShareeLib/TestShareeLib.csproj | 11 +- .../RequestHandler/TestBookedUnknown.cs | 175 +- .../RequestHandler/TestReservedClosed.cs | 78 +- .../RequestHandler/TestReservedUnknown.cs | 46 +- .../ViewModel/TestViewModelHelper.cs | 95 + 257 files changed, 7763 insertions(+), 2861 deletions(-) delete mode 100644 LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldBlockedException.cs create mode 100644 LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldIsBlockedException.cs create mode 100644 LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldWasBlockedException.cs rename Sharee.sln => TINK.sln (87%) create mode 100644 TINK/TINK.Android/Properties/AndroidManifest.xml.bak rename TINK/TINK.Android/{Sharee.Android.csproj => TINK.Android.csproj} (97%) delete mode 100644 TINK/TINK.UWP/App.xaml delete mode 100644 TINK/TINK.UWP/App.xaml.cs delete mode 100644 TINK/TINK.UWP/Assets/LockScreenLogo.scale-100.png delete mode 100644 TINK/TINK.UWP/Assets/LockScreenLogo.scale-125.png delete mode 100644 TINK/TINK.UWP/Assets/LockScreenLogo.scale-150.png delete mode 100644 TINK/TINK.UWP/Assets/LockScreenLogo.scale-200.png delete mode 100644 TINK/TINK.UWP/Assets/LockScreenLogo.scale-400.png delete mode 100644 TINK/TINK.UWP/Assets/SplashScreen.scale-100.png delete mode 100644 TINK/TINK.UWP/Assets/SplashScreen.scale-125.png delete mode 100644 TINK/TINK.UWP/Assets/SplashScreen.scale-150.png delete mode 100644 TINK/TINK.UWP/Assets/SplashScreen.scale-200.png delete mode 100644 TINK/TINK.UWP/Assets/SplashScreen.scale-400.png delete mode 100644 TINK/TINK.UWP/Assets/Square150x150Logo.scale-200.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.scale-100.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.scale-125.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.scale-150.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.scale-200.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.scale-400.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-16_altform-unplated.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-256_altform-unplated.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-32_altform-unplated.png delete mode 100644 TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-48_altform-unplated.png delete mode 100644 TINK/TINK.UWP/Assets/StoreLogo.png delete mode 100644 TINK/TINK.UWP/Assets/Wide310x150Logo.scale-100.png delete mode 100644 TINK/TINK.UWP/Assets/Wide310x150Logo.scale-125.png delete mode 100644 TINK/TINK.UWP/Assets/Wide310x150Logo.scale-150.png delete mode 100644 TINK/TINK.UWP/Assets/Wide310x150Logo.scale-200.png delete mode 100644 TINK/TINK.UWP/Assets/Wide310x150Logo.scale-400.png delete mode 100644 TINK/TINK.UWP/Assets/tink2.png delete mode 100644 TINK/TINK.UWP/Device/WinPhoneDevice .cs delete mode 100644 TINK/TINK.UWP/MainPage.xaml delete mode 100644 TINK/TINK.UWP/MainPage.xaml.cs delete mode 100644 TINK/TINK.UWP/Package.appxmanifest delete mode 100644 TINK/TINK.UWP/Properties/AssemblyInfo.cs delete mode 100644 TINK/TINK.UWP/Properties/Default.rd.xml delete mode 100644 TINK/TINK.UWP/TINK.UWP.csproj delete mode 100644 TINK/TINK.UWP/project.json rename TINK/TINK.iOS/{Sharee.iOS.csproj => TINK.iOS.csproj} (99%) rename TINK/TINK/{Sharee.shproj => TINK.shproj} (100%) create mode 100644 TINKLib/Model/Device/ISmartDevice.cs create mode 100644 TINKLib/Model/User/Account/Store.cs create mode 100644 TINKLib/Repository/CopriCallsMemory.cs create mode 100644 TINKLib/Repository/Exception/UnsupportedCopriVersionDetectedException.cs create mode 100644 TINKLib/Repository/Response/CopriVersion.cs create mode 100644 TINKLib/Repository/Response/GpsInfo.cs rename TINKLib/Repository/Response/{JsonConvert.cs => JsonConvertRethrow.cs} (95%) create mode 100644 TINKLib/Repository/Response/VersionindependentResponse.cs create mode 100644 TINKLib/Services/IServicesContainer.cs rename {TINK/TINK => TINKLib}/ViewModel/FeesAndBikes/HelpContactViewModel.cs (70%) create mode 100644 TestShareeLib/Model/Connector/TestCachetimings.cs create mode 100644 TestShareeLib/Model/Connector/TestConnectorFactory.cs create mode 100644 TestShareeLib/Model/Connector/TestCopriProviderHttps.cs create mode 100644 TestShareeLib/Model/Connector/TestUpdaterJSON.cs create mode 100644 TestShareeLib/Model/TestTinkApp.cs create mode 100644 TestShareeLib/Model/TestTinkAppLogin.cs create mode 100644 TestShareeLib/Repository/TestCopriCallsHttps.cs create mode 100644 TestShareeLib/Repository/TestCopriCallsMonkeyStore.cs create mode 100644 TestShareeLib/Repository/TestCopriCallsStatic.cs create mode 100644 TestShareeLib/ViewModel/TestViewModelHelper.cs diff --git a/LockItBLE/LockItBLE.csproj b/LockItBLE/LockItBLE.csproj index 6b5f44e..abcac32 100644 --- a/LockItBLE/LockItBLE.csproj +++ b/LockItBLE/LockItBLE.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/LockItBLE/Services/BluetoothLock/BLE/LockItEventBased.cs b/LockItBLE/Services/BluetoothLock/BLE/LockItEventBased.cs index e776e2e..c725d9e 100644 --- a/LockItBLE/Services/BluetoothLock/BLE/LockItEventBased.cs +++ b/LockItBLE/Services/BluetoothLock/BLE/LockItEventBased.cs @@ -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 diff --git a/LockItBLE/Services/BluetoothLock/BLE/LockItPolling.cs b/LockItBLE/Services/BluetoothLock/BLE/LockItPolling.cs index 8ec89ac..9b1c43e 100644 --- a/LockItBLE/Services/BluetoothLock/BLE/LockItPolling.cs +++ b/LockItBLE/Services/BluetoothLock/BLE/LockItPolling.cs @@ -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()); } diff --git a/LockItShared/LockItShared.csproj b/LockItShared/LockItShared.csproj index d8df954..49ee5c5 100644 --- a/LockItShared/LockItShared.csproj +++ b/LockItShared/LockItShared.csproj @@ -23,7 +23,7 @@ - + diff --git a/LockItShared/MultilingualResources/LockItShared.de.xlf b/LockItShared/MultilingualResources/LockItShared.de.xlf index 6fe319c..4cb431d 100644 --- a/LockItShared/MultilingualResources/LockItShared.de.xlf +++ b/LockItShared/MultilingualResources/LockItShared.de.xlf @@ -18,11 +18,6 @@ Unexpected locking state "{0}" detected after sending open command. Unerwarteter Schlosszustand "{0}" gemeldet nach Ausführen des Öffnen-Befehls. - - Lock reports unknown bold position. - Schloss meldet unbekannten Schließzustand. - Please verify the translation’s accuracy as the source string was updated after it was translated. - No bluetooth connection. Keine Bluetooth-Verbindung. @@ -39,6 +34,10 @@ Bold is blocked. Schloss ist blockiert. + + Bolds was or is blocked. + Bolds was or is blocked. + diff --git a/LockItShared/MultilingualResources/Resources.Designer.cs b/LockItShared/MultilingualResources/Resources.Designer.cs index 1d94ff4..02847cd 100644 --- a/LockItShared/MultilingualResources/Resources.Designer.cs +++ b/LockItShared/MultilingualResources/Resources.Designer.cs @@ -114,6 +114,15 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Bolds was or is blocked.. + /// + internal static string ErrorOpenLockBoldWasBlocked { + get { + return ResourceManager.GetString("ErrorOpenLockBoldWasBlocked", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unexpected locking state "{0}" detected after sending open command.. /// @@ -122,14 +131,5 @@ namespace TINK.MultilingualResources { return ResourceManager.GetString("ErrorOpenLockUnexpectedState", resourceCulture); } } - - /// - /// Looks up a localized string similar to Lock reports unknown bold position.. - /// - internal static string ErrorOpenLockUnknownPosition { - get { - return ResourceManager.GetString("ErrorOpenLockUnknownPosition", resourceCulture); - } - } } } diff --git a/LockItShared/MultilingualResources/Resources.de.resx b/LockItShared/MultilingualResources/Resources.de.resx index ad7fac7..aa82550 100644 --- a/LockItShared/MultilingualResources/Resources.de.resx +++ b/LockItShared/MultilingualResources/Resources.de.resx @@ -21,9 +21,6 @@ Unerwarteter Schlosszustand "{0}" gemeldet nach Ausführen des Öffnen-Befehls. - - Schloss meldet unbekannten Schließzustand. - Keine Bluetooth-Verbindung. diff --git a/LockItShared/MultilingualResources/Resources.resx b/LockItShared/MultilingualResources/Resources.resx index b1c2481..1114426 100644 --- a/LockItShared/MultilingualResources/Resources.resx +++ b/LockItShared/MultilingualResources/Resources.resx @@ -135,10 +135,10 @@ Bold is blocked. + + Bolds was or is blocked. + Unexpected locking state "{0}" detected after sending open command. - - Lock reports unknown bold position. - \ No newline at end of file diff --git a/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldBlockedException.cs b/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldBlockedException.cs deleted file mode 100644 index 5dd83c2..0000000 --- a/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldBlockedException.cs +++ /dev/null @@ -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) - { - } - } -} diff --git a/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldIsBlockedException.cs b/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldIsBlockedException.cs new file mode 100644 index 0000000..227c6b9 --- /dev/null +++ b/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldIsBlockedException.cs @@ -0,0 +1,16 @@ +using TINK.Model.Bike.BluetoothLock; + +namespace TINK.Services.BluetoothLock.Exception +{ + /// + /// Lock can not be opened, because bold is blocked. Lock reports that obstacle is still blocking. + /// + public class CouldntOpenBoldIsBlockedException : StateAwareException + { + public CouldntOpenBoldIsBlockedException() : base( + LockingState.Unknown, + MultilingualResources.Resources.ErrorOpenLockBoldBlocked) + { + } + } +} diff --git a/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldWasBlockedException.cs b/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldWasBlockedException.cs new file mode 100644 index 0000000..5a8b7ce --- /dev/null +++ b/LockItShared/Services/BluetoothLock/Exception/CouldntOpenBoldWasBlockedException.cs @@ -0,0 +1,16 @@ +using TINK.Model.Bike.BluetoothLock; + +namespace TINK.Services.BluetoothLock.Exception +{ + /// + /// Lock can not be opened, because bold is/ was blocked. Obstacle might no more block but lock could not be opened completely to obstacle. + /// + public class CouldntOpenBoldWasBlockedException : StateAwareException + { + public CouldntOpenBoldWasBlockedException() : base( + LockingState.Unknown, + MultilingualResources.Resources.ErrorOpenLockBoldWasBlocked) + { + } + } +} diff --git a/LockItShared/Services/BluetoothLock/Exception/CouldntOpenInconsistentStateExecption.cs b/LockItShared/Services/BluetoothLock/Exception/CouldntOpenInconsistentStateExecption.cs index 059e46a..54c3f15 100644 --- a/LockItShared/Services/BluetoothLock/Exception/CouldntOpenInconsistentStateExecption.cs +++ b/LockItShared/Services/BluetoothLock/Exception/CouldntOpenInconsistentStateExecption.cs @@ -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)) { } } diff --git a/Sharee.sln b/TINK.sln similarity index 87% rename from Sharee.sln rename to TINK.sln index 034dc5d..4c71a1c 100644 --- a/Sharee.sln +++ b/TINK.sln @@ -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 diff --git a/TINK/TINK.Android/Model/Device/Device.cs b/TINK/TINK.Android/Model/Device/Device.cs index f6a309c..3ee8aaf 100644 --- a/TINK/TINK.Android/Model/Device/Device.cs +++ b/TINK/TINK.Android/Model/Device/Device.cs @@ -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; + /// Gets unitque device identifier. /// Gets the identifies specifying device. - 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); } } \ No newline at end of file diff --git a/TINK/TINK.Android/Properties/AndroidManifest.xml b/TINK/TINK.Android/Properties/AndroidManifest.xml index 61f1e7c..113f8b4 100644 --- a/TINK/TINK.Android/Properties/AndroidManifest.xml +++ b/TINK/TINK.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + @@ -15,6 +15,6 @@ - + \ No newline at end of file diff --git a/TINK/TINK.Android/Properties/AndroidManifest.xml.bak b/TINK/TINK.Android/Properties/AndroidManifest.xml.bak new file mode 100644 index 0000000..61f1e7c --- /dev/null +++ b/TINK/TINK.Android/Properties/AndroidManifest.xml.bak @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TINK/TINK.Android/Resources/Resource.Designer.cs b/TINK/TINK.Android/Resources/Resource.Designer.cs index 8435b5d..a8ab3f0 100644 --- a/TINK/TINK.Android/Resources/Resource.Designer.cs +++ b/TINK/TINK.Android/Resources/Resource.Designer.cs @@ -433,13 +433,7 @@ namespace TINK.Droid global::Xamarin.CommunityToolkit.Resource.Animation.EnterFromRight = global::TINK.Droid.Resource.Animation.EnterFromRight; global::Xamarin.CommunityToolkit.Resource.Animation.ExitToLeft = global::TINK.Droid.Resource.Animation.ExitToLeft; global::Xamarin.CommunityToolkit.Resource.Animation.ExitToRight = global::TINK.Droid.Resource.Animation.ExitToRight; - global::Xamarin.CommunityToolkit.Resource.Animation.fragment_close_enter = global::TINK.Droid.Resource.Animation.fragment_close_enter; - global::Xamarin.CommunityToolkit.Resource.Animation.fragment_close_exit = global::TINK.Droid.Resource.Animation.fragment_close_exit; - global::Xamarin.CommunityToolkit.Resource.Animation.fragment_fade_enter = global::TINK.Droid.Resource.Animation.fragment_fade_enter; - global::Xamarin.CommunityToolkit.Resource.Animation.fragment_fade_exit = global::TINK.Droid.Resource.Animation.fragment_fade_exit; global::Xamarin.CommunityToolkit.Resource.Animation.fragment_fast_out_extra_slow_in = global::TINK.Droid.Resource.Animation.fragment_fast_out_extra_slow_in; - global::Xamarin.CommunityToolkit.Resource.Animation.fragment_open_enter = global::TINK.Droid.Resource.Animation.fragment_open_enter; - global::Xamarin.CommunityToolkit.Resource.Animation.fragment_open_exit = global::TINK.Droid.Resource.Animation.fragment_open_exit; global::Xamarin.CommunityToolkit.Resource.Animation.mtrl_bottom_sheet_slide_in = global::TINK.Droid.Resource.Animation.mtrl_bottom_sheet_slide_in; global::Xamarin.CommunityToolkit.Resource.Animation.mtrl_bottom_sheet_slide_out = global::TINK.Droid.Resource.Animation.mtrl_bottom_sheet_slide_out; global::Xamarin.CommunityToolkit.Resource.Animation.mtrl_card_lowers_interpolator = global::TINK.Droid.Resource.Animation.mtrl_card_lowers_interpolator; @@ -4079,13 +4073,7 @@ namespace TINK.Droid global::Xamarin.Forms.Platform.Android.Resource.Animation.EnterFromRight = global::TINK.Droid.Resource.Animation.EnterFromRight; global::Xamarin.Forms.Platform.Android.Resource.Animation.ExitToLeft = global::TINK.Droid.Resource.Animation.ExitToLeft; global::Xamarin.Forms.Platform.Android.Resource.Animation.ExitToRight = global::TINK.Droid.Resource.Animation.ExitToRight; - global::Xamarin.Forms.Platform.Android.Resource.Animation.fragment_close_enter = global::TINK.Droid.Resource.Animation.fragment_close_enter; - global::Xamarin.Forms.Platform.Android.Resource.Animation.fragment_close_exit = global::TINK.Droid.Resource.Animation.fragment_close_exit; - global::Xamarin.Forms.Platform.Android.Resource.Animation.fragment_fade_enter = global::TINK.Droid.Resource.Animation.fragment_fade_enter; - global::Xamarin.Forms.Platform.Android.Resource.Animation.fragment_fade_exit = global::TINK.Droid.Resource.Animation.fragment_fade_exit; global::Xamarin.Forms.Platform.Android.Resource.Animation.fragment_fast_out_extra_slow_in = global::TINK.Droid.Resource.Animation.fragment_fast_out_extra_slow_in; - global::Xamarin.Forms.Platform.Android.Resource.Animation.fragment_open_enter = global::TINK.Droid.Resource.Animation.fragment_open_enter; - global::Xamarin.Forms.Platform.Android.Resource.Animation.fragment_open_exit = global::TINK.Droid.Resource.Animation.fragment_open_exit; global::Xamarin.Forms.Platform.Android.Resource.Animation.mtrl_bottom_sheet_slide_in = global::TINK.Droid.Resource.Animation.mtrl_bottom_sheet_slide_in; global::Xamarin.Forms.Platform.Android.Resource.Animation.mtrl_bottom_sheet_slide_out = global::TINK.Droid.Resource.Animation.mtrl_bottom_sheet_slide_out; global::Xamarin.Forms.Platform.Android.Resource.Animation.mtrl_card_lowers_interpolator = global::TINK.Droid.Resource.Animation.mtrl_card_lowers_interpolator; @@ -7704,13 +7692,7 @@ namespace TINK.Droid global::Xamarin.Forms.Platform.Resource.Animation.EnterFromRight = global::TINK.Droid.Resource.Animation.EnterFromRight; global::Xamarin.Forms.Platform.Resource.Animation.ExitToLeft = global::TINK.Droid.Resource.Animation.ExitToLeft; global::Xamarin.Forms.Platform.Resource.Animation.ExitToRight = global::TINK.Droid.Resource.Animation.ExitToRight; - global::Xamarin.Forms.Platform.Resource.Animation.fragment_close_enter = global::TINK.Droid.Resource.Animation.fragment_close_enter; - global::Xamarin.Forms.Platform.Resource.Animation.fragment_close_exit = global::TINK.Droid.Resource.Animation.fragment_close_exit; - global::Xamarin.Forms.Platform.Resource.Animation.fragment_fade_enter = global::TINK.Droid.Resource.Animation.fragment_fade_enter; - global::Xamarin.Forms.Platform.Resource.Animation.fragment_fade_exit = global::TINK.Droid.Resource.Animation.fragment_fade_exit; global::Xamarin.Forms.Platform.Resource.Animation.fragment_fast_out_extra_slow_in = global::TINK.Droid.Resource.Animation.fragment_fast_out_extra_slow_in; - global::Xamarin.Forms.Platform.Resource.Animation.fragment_open_enter = global::TINK.Droid.Resource.Animation.fragment_open_enter; - global::Xamarin.Forms.Platform.Resource.Animation.fragment_open_exit = global::TINK.Droid.Resource.Animation.fragment_open_exit; global::Xamarin.Forms.Platform.Resource.Animation.mtrl_bottom_sheet_slide_in = global::TINK.Droid.Resource.Animation.mtrl_bottom_sheet_slide_in; global::Xamarin.Forms.Platform.Resource.Animation.mtrl_bottom_sheet_slide_out = global::TINK.Droid.Resource.Animation.mtrl_bottom_sheet_slide_out; global::Xamarin.Forms.Platform.Resource.Animation.mtrl_card_lowers_interpolator = global::TINK.Droid.Resource.Animation.mtrl_card_lowers_interpolator; @@ -11399,40 +11381,22 @@ namespace TINK.Droid public const int ExitToRight = 2130771999; // aapt resource value: 0x7F010020 - public const int fragment_close_enter = 2130772000; + public const int fragment_fast_out_extra_slow_in = 2130772000; // aapt resource value: 0x7F010021 - public const int fragment_close_exit = 2130772001; + public const int mtrl_bottom_sheet_slide_in = 2130772001; // aapt resource value: 0x7F010022 - public const int fragment_fade_enter = 2130772002; + public const int mtrl_bottom_sheet_slide_out = 2130772002; // aapt resource value: 0x7F010023 - public const int fragment_fade_exit = 2130772003; + public const int mtrl_card_lowers_interpolator = 2130772003; // aapt resource value: 0x7F010024 - public const int fragment_fast_out_extra_slow_in = 2130772004; + public const int slide_in_right = 2130772004; // aapt resource value: 0x7F010025 - public const int fragment_open_enter = 2130772005; - - // aapt resource value: 0x7F010026 - public const int fragment_open_exit = 2130772006; - - // aapt resource value: 0x7F010027 - public const int mtrl_bottom_sheet_slide_in = 2130772007; - - // aapt resource value: 0x7F010028 - public const int mtrl_bottom_sheet_slide_out = 2130772008; - - // aapt resource value: 0x7F010029 - public const int mtrl_card_lowers_interpolator = 2130772009; - - // aapt resource value: 0x7F01002A - public const int slide_in_right = 2130772010; - - // aapt resource value: 0x7F01002B - public const int slide_out_left = 2130772011; + public const int slide_out_left = 2130772005; static Animation() { @@ -11457,40 +11421,58 @@ namespace TINK.Droid public const int design_fab_show_motion_spec = 2130837506; // aapt resource value: 0x7F020003 - public const int mtrl_btn_state_list_anim = 2130837507; + public const int fragment_close_enter = 2130837507; // aapt resource value: 0x7F020004 - public const int mtrl_btn_unelevated_state_list_anim = 2130837508; + public const int fragment_close_exit = 2130837508; // aapt resource value: 0x7F020005 - public const int mtrl_card_state_list_anim = 2130837509; + public const int fragment_fade_enter = 2130837509; // aapt resource value: 0x7F020006 - public const int mtrl_chip_state_list_anim = 2130837510; + public const int fragment_fade_exit = 2130837510; // aapt resource value: 0x7F020007 - public const int mtrl_extended_fab_change_size_motion_spec = 2130837511; + public const int fragment_open_enter = 2130837511; // aapt resource value: 0x7F020008 - public const int mtrl_extended_fab_hide_motion_spec = 2130837512; + public const int fragment_open_exit = 2130837512; // aapt resource value: 0x7F020009 - public const int mtrl_extended_fab_show_motion_spec = 2130837513; + public const int mtrl_btn_state_list_anim = 2130837513; // aapt resource value: 0x7F02000A - public const int mtrl_extended_fab_state_list_animator = 2130837514; + public const int mtrl_btn_unelevated_state_list_anim = 2130837514; // aapt resource value: 0x7F02000B - public const int mtrl_fab_hide_motion_spec = 2130837515; + public const int mtrl_card_state_list_anim = 2130837515; // aapt resource value: 0x7F02000C - public const int mtrl_fab_show_motion_spec = 2130837516; + public const int mtrl_chip_state_list_anim = 2130837516; // aapt resource value: 0x7F02000D - public const int mtrl_fab_transformation_sheet_collapse_spec = 2130837517; + public const int mtrl_extended_fab_change_size_motion_spec = 2130837517; // aapt resource value: 0x7F02000E - public const int mtrl_fab_transformation_sheet_expand_spec = 2130837518; + public const int mtrl_extended_fab_hide_motion_spec = 2130837518; + + // aapt resource value: 0x7F02000F + public const int mtrl_extended_fab_show_motion_spec = 2130837519; + + // aapt resource value: 0x7F020010 + public const int mtrl_extended_fab_state_list_animator = 2130837520; + + // aapt resource value: 0x7F020011 + public const int mtrl_fab_hide_motion_spec = 2130837521; + + // aapt resource value: 0x7F020012 + public const int mtrl_fab_show_motion_spec = 2130837522; + + // aapt resource value: 0x7F020013 + public const int mtrl_fab_transformation_sheet_collapse_spec = 2130837523; + + // aapt resource value: 0x7F020014 + public const int mtrl_fab_transformation_sheet_expand_spec = 2130837524; static Animator() { @@ -12427,1201 +12409,1204 @@ namespace TINK.Droid public const int fontProviderQuery = 2130903346; // aapt resource value: 0x7F030133 - public const int fontStyle = 2130903347; + public const int fontProviderSystemFontFamily = 2130903347; // aapt resource value: 0x7F030134 - public const int fontVariationSettings = 2130903348; + public const int fontStyle = 2130903348; // aapt resource value: 0x7F030135 - public const int fontWeight = 2130903349; + public const int fontVariationSettings = 2130903349; // aapt resource value: 0x7F030136 - public const int foregroundInsidePadding = 2130903350; + public const int fontWeight = 2130903350; // aapt resource value: 0x7F030137 - public const int gapBetweenBars = 2130903351; + public const int foregroundInsidePadding = 2130903351; // aapt resource value: 0x7F030138 - public const int gestureInsetBottomIgnored = 2130903352; + public const int gapBetweenBars = 2130903352; // aapt resource value: 0x7F030139 - public const int goIcon = 2130903353; + public const int gestureInsetBottomIgnored = 2130903353; // aapt resource value: 0x7F03013A - public const int haloColor = 2130903354; + public const int goIcon = 2130903354; // aapt resource value: 0x7F03013B - public const int haloRadius = 2130903355; + public const int haloColor = 2130903355; // aapt resource value: 0x7F03013C - public const int headerLayout = 2130903356; + public const int haloRadius = 2130903356; // aapt resource value: 0x7F03013D - public const int height = 2130903357; + public const int headerLayout = 2130903357; // aapt resource value: 0x7F03013E - public const int helperText = 2130903358; + public const int height = 2130903358; // aapt resource value: 0x7F03013F - public const int helperTextEnabled = 2130903359; + public const int helperText = 2130903359; // aapt resource value: 0x7F030140 - public const int helperTextTextAppearance = 2130903360; + public const int helperTextEnabled = 2130903360; // aapt resource value: 0x7F030141 - public const int helperTextTextColor = 2130903361; + public const int helperTextTextAppearance = 2130903361; // aapt resource value: 0x7F030142 - public const int hideMotionSpec = 2130903362; + public const int helperTextTextColor = 2130903362; // aapt resource value: 0x7F030143 - public const int hideOnContentScroll = 2130903363; + public const int hideMotionSpec = 2130903363; // aapt resource value: 0x7F030144 - public const int hideOnScroll = 2130903364; + public const int hideOnContentScroll = 2130903364; // aapt resource value: 0x7F030145 - public const int hintAnimationEnabled = 2130903365; + public const int hideOnScroll = 2130903365; // aapt resource value: 0x7F030146 - public const int hintEnabled = 2130903366; + public const int hintAnimationEnabled = 2130903366; // aapt resource value: 0x7F030147 - public const int hintTextAppearance = 2130903367; + public const int hintEnabled = 2130903367; // aapt resource value: 0x7F030148 - public const int hintTextColor = 2130903368; + public const int hintTextAppearance = 2130903368; // aapt resource value: 0x7F030149 - public const int homeAsUpIndicator = 2130903369; + public const int hintTextColor = 2130903369; // aapt resource value: 0x7F03014A - public const int homeLayout = 2130903370; + public const int homeAsUpIndicator = 2130903370; // aapt resource value: 0x7F03014B - public const int horizontalOffset = 2130903371; + public const int homeLayout = 2130903371; // aapt resource value: 0x7F03014C - public const int hoveredFocusedTranslationZ = 2130903372; + public const int horizontalOffset = 2130903372; // aapt resource value: 0x7F03014D - public const int icon = 2130903373; + public const int hoveredFocusedTranslationZ = 2130903373; // aapt resource value: 0x7F03014E - public const int iconEndPadding = 2130903374; + public const int icon = 2130903374; // aapt resource value: 0x7F03014F - public const int iconGravity = 2130903375; - - // aapt resource value: 0x7F030155 - public const int iconifiedByDefault = 2130903381; + public const int iconEndPadding = 2130903375; // aapt resource value: 0x7F030150 - public const int iconPadding = 2130903376; - - // aapt resource value: 0x7F030151 - public const int iconSize = 2130903377; - - // aapt resource value: 0x7F030152 - public const int iconStartPadding = 2130903378; - - // aapt resource value: 0x7F030153 - public const int iconTint = 2130903379; - - // aapt resource value: 0x7F030154 - public const int iconTintMode = 2130903380; + public const int iconGravity = 2130903376; // aapt resource value: 0x7F030156 - public const int imageAspectRatio = 2130903382; + public const int iconifiedByDefault = 2130903382; + + // aapt resource value: 0x7F030151 + public const int iconPadding = 2130903377; + + // aapt resource value: 0x7F030152 + public const int iconSize = 2130903378; + + // aapt resource value: 0x7F030153 + public const int iconStartPadding = 2130903379; + + // aapt resource value: 0x7F030154 + public const int iconTint = 2130903380; + + // aapt resource value: 0x7F030155 + public const int iconTintMode = 2130903381; // aapt resource value: 0x7F030157 - public const int imageAspectRatioAdjust = 2130903383; + public const int imageAspectRatio = 2130903383; // aapt resource value: 0x7F030158 - public const int imageButtonStyle = 2130903384; + public const int imageAspectRatioAdjust = 2130903384; // aapt resource value: 0x7F030159 - public const int indeterminateProgressStyle = 2130903385; + public const int imageButtonStyle = 2130903385; // aapt resource value: 0x7F03015A - public const int initialActivityCount = 2130903386; + public const int indeterminateProgressStyle = 2130903386; // aapt resource value: 0x7F03015B - public const int insetForeground = 2130903387; + public const int initialActivityCount = 2130903387; // aapt resource value: 0x7F03015C - public const int isLightTheme = 2130903388; + public const int insetForeground = 2130903388; // aapt resource value: 0x7F03015D - public const int isMaterialTheme = 2130903389; + public const int isLightTheme = 2130903389; // aapt resource value: 0x7F03015E - public const int itemBackground = 2130903390; + public const int isMaterialTheme = 2130903390; // aapt resource value: 0x7F03015F - public const int itemFillColor = 2130903391; + public const int itemBackground = 2130903391; // aapt resource value: 0x7F030160 - public const int itemHorizontalPadding = 2130903392; + public const int itemFillColor = 2130903392; // aapt resource value: 0x7F030161 - public const int itemHorizontalTranslationEnabled = 2130903393; + public const int itemHorizontalPadding = 2130903393; // aapt resource value: 0x7F030162 - public const int itemIconPadding = 2130903394; + public const int itemHorizontalTranslationEnabled = 2130903394; // aapt resource value: 0x7F030163 - public const int itemIconSize = 2130903395; + public const int itemIconPadding = 2130903395; // aapt resource value: 0x7F030164 - public const int itemIconTint = 2130903396; + public const int itemIconSize = 2130903396; // aapt resource value: 0x7F030165 - public const int itemMaxLines = 2130903397; + public const int itemIconTint = 2130903397; // aapt resource value: 0x7F030166 - public const int itemPadding = 2130903398; + public const int itemMaxLines = 2130903398; // aapt resource value: 0x7F030167 - public const int itemRippleColor = 2130903399; + public const int itemPadding = 2130903399; // aapt resource value: 0x7F030168 - public const int itemShapeAppearance = 2130903400; + public const int itemRippleColor = 2130903400; // aapt resource value: 0x7F030169 - public const int itemShapeAppearanceOverlay = 2130903401; + public const int itemShapeAppearance = 2130903401; // aapt resource value: 0x7F03016A - public const int itemShapeFillColor = 2130903402; + public const int itemShapeAppearanceOverlay = 2130903402; // aapt resource value: 0x7F03016B - public const int itemShapeInsetBottom = 2130903403; + public const int itemShapeFillColor = 2130903403; // aapt resource value: 0x7F03016C - public const int itemShapeInsetEnd = 2130903404; + public const int itemShapeInsetBottom = 2130903404; // aapt resource value: 0x7F03016D - public const int itemShapeInsetStart = 2130903405; + public const int itemShapeInsetEnd = 2130903405; // aapt resource value: 0x7F03016E - public const int itemShapeInsetTop = 2130903406; + public const int itemShapeInsetStart = 2130903406; // aapt resource value: 0x7F03016F - public const int itemSpacing = 2130903407; + public const int itemShapeInsetTop = 2130903407; // aapt resource value: 0x7F030170 - public const int itemStrokeColor = 2130903408; + public const int itemSpacing = 2130903408; // aapt resource value: 0x7F030171 - public const int itemStrokeWidth = 2130903409; + public const int itemStrokeColor = 2130903409; // aapt resource value: 0x7F030172 - public const int itemTextAppearance = 2130903410; + public const int itemStrokeWidth = 2130903410; // aapt resource value: 0x7F030173 - public const int itemTextAppearanceActive = 2130903411; + public const int itemTextAppearance = 2130903411; // aapt resource value: 0x7F030174 - public const int itemTextAppearanceInactive = 2130903412; + public const int itemTextAppearanceActive = 2130903412; // aapt resource value: 0x7F030175 - public const int itemTextColor = 2130903413; + public const int itemTextAppearanceInactive = 2130903413; // aapt resource value: 0x7F030176 - public const int keylines = 2130903414; + public const int itemTextColor = 2130903414; // aapt resource value: 0x7F030177 - public const int labelBehavior = 2130903415; + public const int keylines = 2130903415; // aapt resource value: 0x7F030178 - public const int labelStyle = 2130903416; + public const int labelBehavior = 2130903416; // aapt resource value: 0x7F030179 - public const int labelVisibilityMode = 2130903417; + public const int labelStyle = 2130903417; // aapt resource value: 0x7F03017A - public const int lastBaselineToBottomHeight = 2130903418; + public const int labelVisibilityMode = 2130903418; // aapt resource value: 0x7F03017B - public const int latLngBoundsNorthEastLatitude = 2130903419; + public const int lastBaselineToBottomHeight = 2130903419; // aapt resource value: 0x7F03017C - public const int latLngBoundsNorthEastLongitude = 2130903420; + public const int latLngBoundsNorthEastLatitude = 2130903420; // aapt resource value: 0x7F03017D - public const int latLngBoundsSouthWestLatitude = 2130903421; + public const int latLngBoundsNorthEastLongitude = 2130903421; // aapt resource value: 0x7F03017E - public const int latLngBoundsSouthWestLongitude = 2130903422; + public const int latLngBoundsSouthWestLatitude = 2130903422; // aapt resource value: 0x7F03017F - public const int layout = 2130903423; + public const int latLngBoundsSouthWestLongitude = 2130903423; // aapt resource value: 0x7F030180 - public const int layoutManager = 2130903424; + public const int layout = 2130903424; // aapt resource value: 0x7F030181 - public const int layout_anchor = 2130903425; + public const int layoutManager = 2130903425; // aapt resource value: 0x7F030182 - public const int layout_anchorGravity = 2130903426; + public const int layout_anchor = 2130903426; // aapt resource value: 0x7F030183 - public const int layout_behavior = 2130903427; + public const int layout_anchorGravity = 2130903427; // aapt resource value: 0x7F030184 - public const int layout_collapseMode = 2130903428; + public const int layout_behavior = 2130903428; // aapt resource value: 0x7F030185 - public const int layout_collapseParallaxMultiplier = 2130903429; + public const int layout_collapseMode = 2130903429; // aapt resource value: 0x7F030186 - public const int layout_dodgeInsetEdges = 2130903430; + public const int layout_collapseParallaxMultiplier = 2130903430; // aapt resource value: 0x7F030187 - public const int layout_insetEdge = 2130903431; + public const int layout_dodgeInsetEdges = 2130903431; // aapt resource value: 0x7F030188 - public const int layout_keyline = 2130903432; + public const int layout_insetEdge = 2130903432; // aapt resource value: 0x7F030189 - public const int layout_scrollFlags = 2130903433; + public const int layout_keyline = 2130903433; // aapt resource value: 0x7F03018A - public const int layout_scrollInterpolator = 2130903434; + public const int layout_scrollFlags = 2130903434; // aapt resource value: 0x7F03018B - public const int liftOnScroll = 2130903435; + public const int layout_scrollInterpolator = 2130903435; // aapt resource value: 0x7F03018C - public const int liftOnScrollTargetViewId = 2130903436; + public const int liftOnScroll = 2130903436; // aapt resource value: 0x7F03018D - public const int lineHeight = 2130903437; + public const int liftOnScrollTargetViewId = 2130903437; // aapt resource value: 0x7F03018E - public const int lineSpacing = 2130903438; + public const int lineHeight = 2130903438; // aapt resource value: 0x7F03018F - public const int listChoiceBackgroundIndicator = 2130903439; + public const int lineSpacing = 2130903439; // aapt resource value: 0x7F030190 - public const int listChoiceIndicatorMultipleAnimated = 2130903440; + public const int listChoiceBackgroundIndicator = 2130903440; // aapt resource value: 0x7F030191 - public const int listChoiceIndicatorSingleAnimated = 2130903441; + public const int listChoiceIndicatorMultipleAnimated = 2130903441; // aapt resource value: 0x7F030192 - public const int listDividerAlertDialog = 2130903442; + public const int listChoiceIndicatorSingleAnimated = 2130903442; // aapt resource value: 0x7F030193 - public const int listItemLayout = 2130903443; + public const int listDividerAlertDialog = 2130903443; // aapt resource value: 0x7F030194 - public const int listLayout = 2130903444; + public const int listItemLayout = 2130903444; // aapt resource value: 0x7F030195 - public const int listMenuViewStyle = 2130903445; + public const int listLayout = 2130903445; // aapt resource value: 0x7F030196 - public const int listPopupWindowStyle = 2130903446; + public const int listMenuViewStyle = 2130903446; // aapt resource value: 0x7F030197 - public const int listPreferredItemHeight = 2130903447; + public const int listPopupWindowStyle = 2130903447; // aapt resource value: 0x7F030198 - public const int listPreferredItemHeightLarge = 2130903448; + public const int listPreferredItemHeight = 2130903448; // aapt resource value: 0x7F030199 - public const int listPreferredItemHeightSmall = 2130903449; + public const int listPreferredItemHeightLarge = 2130903449; // aapt resource value: 0x7F03019A - public const int listPreferredItemPaddingEnd = 2130903450; + public const int listPreferredItemHeightSmall = 2130903450; // aapt resource value: 0x7F03019B - public const int listPreferredItemPaddingLeft = 2130903451; + public const int listPreferredItemPaddingEnd = 2130903451; // aapt resource value: 0x7F03019C - public const int listPreferredItemPaddingRight = 2130903452; + public const int listPreferredItemPaddingLeft = 2130903452; // aapt resource value: 0x7F03019D - public const int listPreferredItemPaddingStart = 2130903453; + public const int listPreferredItemPaddingRight = 2130903453; // aapt resource value: 0x7F03019E - public const int liteMode = 2130903454; + public const int listPreferredItemPaddingStart = 2130903454; // aapt resource value: 0x7F03019F - public const int logo = 2130903455; + public const int liteMode = 2130903455; // aapt resource value: 0x7F0301A0 - public const int logoDescription = 2130903456; + public const int logo = 2130903456; // aapt resource value: 0x7F0301A1 - public const int mapType = 2130903457; + public const int logoDescription = 2130903457; // aapt resource value: 0x7F0301A2 - public const int materialAlertDialogBodyTextStyle = 2130903458; + public const int mapType = 2130903458; // aapt resource value: 0x7F0301A3 - public const int materialAlertDialogTheme = 2130903459; + public const int materialAlertDialogBodyTextStyle = 2130903459; // aapt resource value: 0x7F0301A4 - public const int materialAlertDialogTitleIconStyle = 2130903460; + public const int materialAlertDialogTheme = 2130903460; // aapt resource value: 0x7F0301A5 - public const int materialAlertDialogTitlePanelStyle = 2130903461; + public const int materialAlertDialogTitleIconStyle = 2130903461; // aapt resource value: 0x7F0301A6 - public const int materialAlertDialogTitleTextStyle = 2130903462; + public const int materialAlertDialogTitlePanelStyle = 2130903462; // aapt resource value: 0x7F0301A7 - public const int materialButtonOutlinedStyle = 2130903463; + public const int materialAlertDialogTitleTextStyle = 2130903463; // aapt resource value: 0x7F0301A8 - public const int materialButtonStyle = 2130903464; + public const int materialButtonOutlinedStyle = 2130903464; // aapt resource value: 0x7F0301A9 - public const int materialButtonToggleGroupStyle = 2130903465; + public const int materialButtonStyle = 2130903465; // aapt resource value: 0x7F0301AA - public const int materialCalendarDay = 2130903466; + public const int materialButtonToggleGroupStyle = 2130903466; // aapt resource value: 0x7F0301AB - public const int materialCalendarFullscreenTheme = 2130903467; + public const int materialCalendarDay = 2130903467; // aapt resource value: 0x7F0301AC - public const int materialCalendarHeaderConfirmButton = 2130903468; + public const int materialCalendarFullscreenTheme = 2130903468; // aapt resource value: 0x7F0301AD - public const int materialCalendarHeaderDivider = 2130903469; + public const int materialCalendarHeaderConfirmButton = 2130903469; // aapt resource value: 0x7F0301AE - public const int materialCalendarHeaderLayout = 2130903470; + public const int materialCalendarHeaderDivider = 2130903470; // aapt resource value: 0x7F0301AF - public const int materialCalendarHeaderSelection = 2130903471; + public const int materialCalendarHeaderLayout = 2130903471; // aapt resource value: 0x7F0301B0 - public const int materialCalendarHeaderTitle = 2130903472; + public const int materialCalendarHeaderSelection = 2130903472; // aapt resource value: 0x7F0301B1 - public const int materialCalendarHeaderToggleButton = 2130903473; + public const int materialCalendarHeaderTitle = 2130903473; // aapt resource value: 0x7F0301B2 - public const int materialCalendarStyle = 2130903474; + public const int materialCalendarHeaderToggleButton = 2130903474; // aapt resource value: 0x7F0301B3 - public const int materialCalendarTheme = 2130903475; + public const int materialCalendarStyle = 2130903475; // aapt resource value: 0x7F0301B4 - public const int materialCardViewStyle = 2130903476; + public const int materialCalendarTheme = 2130903476; // aapt resource value: 0x7F0301B5 - public const int materialThemeOverlay = 2130903477; + public const int materialCardViewStyle = 2130903477; // aapt resource value: 0x7F0301B6 - public const int maxActionInlineWidth = 2130903478; + public const int materialThemeOverlay = 2130903478; // aapt resource value: 0x7F0301B7 - public const int maxButtonHeight = 2130903479; + public const int maxActionInlineWidth = 2130903479; // aapt resource value: 0x7F0301B8 - public const int maxCharacterCount = 2130903480; + public const int maxButtonHeight = 2130903480; // aapt resource value: 0x7F0301B9 - public const int maxImageSize = 2130903481; + public const int maxCharacterCount = 2130903481; // aapt resource value: 0x7F0301BA - public const int maxLines = 2130903482; + public const int maxImageSize = 2130903482; // aapt resource value: 0x7F0301BB - public const int measureWithLargestChild = 2130903483; + public const int maxLines = 2130903483; // aapt resource value: 0x7F0301BC - public const int mediaRouteAudioTrackDrawable = 2130903484; + public const int measureWithLargestChild = 2130903484; // aapt resource value: 0x7F0301BD - public const int mediaRouteBodyTextAppearance = 2130903485; + public const int mediaRouteAudioTrackDrawable = 2130903485; // aapt resource value: 0x7F0301BE - public const int mediaRouteButtonStyle = 2130903486; + public const int mediaRouteBodyTextAppearance = 2130903486; // aapt resource value: 0x7F0301BF - public const int mediaRouteButtonTint = 2130903487; + public const int mediaRouteButtonStyle = 2130903487; // aapt resource value: 0x7F0301C0 - public const int mediaRouteCloseDrawable = 2130903488; + public const int mediaRouteButtonTint = 2130903488; // aapt resource value: 0x7F0301C1 - public const int mediaRouteControlPanelThemeOverlay = 2130903489; + public const int mediaRouteCloseDrawable = 2130903489; // aapt resource value: 0x7F0301C2 - public const int mediaRouteDefaultIconDrawable = 2130903490; + public const int mediaRouteControlPanelThemeOverlay = 2130903490; // aapt resource value: 0x7F0301C3 - public const int mediaRouteDividerColor = 2130903491; + public const int mediaRouteDefaultIconDrawable = 2130903491; // aapt resource value: 0x7F0301C4 - public const int mediaRouteHeaderTextAppearance = 2130903492; + public const int mediaRouteDividerColor = 2130903492; // aapt resource value: 0x7F0301C5 - public const int mediaRoutePauseDrawable = 2130903493; + public const int mediaRouteHeaderTextAppearance = 2130903493; // aapt resource value: 0x7F0301C6 - public const int mediaRoutePlayDrawable = 2130903494; + public const int mediaRoutePauseDrawable = 2130903494; // aapt resource value: 0x7F0301C7 - public const int mediaRouteSpeakerGroupIconDrawable = 2130903495; + public const int mediaRoutePlayDrawable = 2130903495; // aapt resource value: 0x7F0301C8 - public const int mediaRouteSpeakerIconDrawable = 2130903496; + public const int mediaRouteSpeakerGroupIconDrawable = 2130903496; // aapt resource value: 0x7F0301C9 - public const int mediaRouteStopDrawable = 2130903497; + public const int mediaRouteSpeakerIconDrawable = 2130903497; // aapt resource value: 0x7F0301CA - public const int mediaRouteTheme = 2130903498; + public const int mediaRouteStopDrawable = 2130903498; // aapt resource value: 0x7F0301CB - public const int mediaRouteTvIconDrawable = 2130903499; + public const int mediaRouteTheme = 2130903499; // aapt resource value: 0x7F0301CC - public const int menu = 2130903500; + public const int mediaRouteTvIconDrawable = 2130903500; // aapt resource value: 0x7F0301CD - public const int minTouchTargetSize = 2130903501; + public const int menu = 2130903501; // aapt resource value: 0x7F0301CE - public const int multiChoiceItemLayout = 2130903502; + public const int minTouchTargetSize = 2130903502; // aapt resource value: 0x7F0301CF - public const int navigationContentDescription = 2130903503; + public const int multiChoiceItemLayout = 2130903503; // aapt resource value: 0x7F0301D0 - public const int navigationIcon = 2130903504; + public const int navigationContentDescription = 2130903504; // aapt resource value: 0x7F0301D1 - public const int navigationMode = 2130903505; + public const int navigationIcon = 2130903505; // aapt resource value: 0x7F0301D2 - public const int navigationViewStyle = 2130903506; + public const int navigationMode = 2130903506; // aapt resource value: 0x7F0301D3 - public const int number = 2130903507; + public const int navigationViewStyle = 2130903507; // aapt resource value: 0x7F0301D4 - public const int numericModifiers = 2130903508; + public const int number = 2130903508; // aapt resource value: 0x7F0301D5 - public const int overlapAnchor = 2130903509; + public const int numericModifiers = 2130903509; // aapt resource value: 0x7F0301D6 - public const int paddingBottomNoButtons = 2130903510; + public const int overlapAnchor = 2130903510; // aapt resource value: 0x7F0301D7 - public const int paddingBottomSystemWindowInsets = 2130903511; + public const int paddingBottomNoButtons = 2130903511; // aapt resource value: 0x7F0301D8 - public const int paddingEnd = 2130903512; + public const int paddingBottomSystemWindowInsets = 2130903512; // aapt resource value: 0x7F0301D9 - public const int paddingLeftSystemWindowInsets = 2130903513; + public const int paddingEnd = 2130903513; // aapt resource value: 0x7F0301DA - public const int paddingRightSystemWindowInsets = 2130903514; + public const int paddingLeftSystemWindowInsets = 2130903514; // aapt resource value: 0x7F0301DB - public const int paddingStart = 2130903515; + public const int paddingRightSystemWindowInsets = 2130903515; // aapt resource value: 0x7F0301DC - public const int paddingTopNoTitle = 2130903516; + public const int paddingStart = 2130903516; // aapt resource value: 0x7F0301DD - public const int panelBackground = 2130903517; + public const int paddingTopNoTitle = 2130903517; // aapt resource value: 0x7F0301DE - public const int panelMenuListTheme = 2130903518; + public const int panelBackground = 2130903518; // aapt resource value: 0x7F0301DF - public const int panelMenuListWidth = 2130903519; + public const int panelMenuListTheme = 2130903519; // aapt resource value: 0x7F0301E0 - public const int passwordToggleContentDescription = 2130903520; + public const int panelMenuListWidth = 2130903520; // aapt resource value: 0x7F0301E1 - public const int passwordToggleDrawable = 2130903521; + public const int passwordToggleContentDescription = 2130903521; // aapt resource value: 0x7F0301E2 - public const int passwordToggleEnabled = 2130903522; + public const int passwordToggleDrawable = 2130903522; // aapt resource value: 0x7F0301E3 - public const int passwordToggleTint = 2130903523; + public const int passwordToggleEnabled = 2130903523; // aapt resource value: 0x7F0301E4 - public const int passwordToggleTintMode = 2130903524; + public const int passwordToggleTint = 2130903524; // aapt resource value: 0x7F0301E5 - public const int placeholderText = 2130903525; + public const int passwordToggleTintMode = 2130903525; // aapt resource value: 0x7F0301E6 - public const int placeholderTextAppearance = 2130903526; + public const int placeholderText = 2130903526; // aapt resource value: 0x7F0301E7 - public const int placeholderTextColor = 2130903527; + public const int placeholderTextAppearance = 2130903527; // aapt resource value: 0x7F0301E8 - public const int popupMenuBackground = 2130903528; + public const int placeholderTextColor = 2130903528; // aapt resource value: 0x7F0301E9 - public const int popupMenuStyle = 2130903529; + public const int popupMenuBackground = 2130903529; // aapt resource value: 0x7F0301EA - public const int popupTheme = 2130903530; + public const int popupMenuStyle = 2130903530; // aapt resource value: 0x7F0301EB - public const int popupWindowStyle = 2130903531; + public const int popupTheme = 2130903531; // aapt resource value: 0x7F0301EC - public const int prefixText = 2130903532; + public const int popupWindowStyle = 2130903532; // aapt resource value: 0x7F0301ED - public const int prefixTextAppearance = 2130903533; + public const int prefixText = 2130903533; // aapt resource value: 0x7F0301EE - public const int prefixTextColor = 2130903534; + public const int prefixTextAppearance = 2130903534; // aapt resource value: 0x7F0301EF - public const int preserveIconSpacing = 2130903535; + public const int prefixTextColor = 2130903535; // aapt resource value: 0x7F0301F0 - public const int pressedTranslationZ = 2130903536; + public const int preserveIconSpacing = 2130903536; // aapt resource value: 0x7F0301F1 - public const int progressBarPadding = 2130903537; + public const int pressedTranslationZ = 2130903537; // aapt resource value: 0x7F0301F2 - public const int progressBarStyle = 2130903538; + public const int progressBarPadding = 2130903538; // aapt resource value: 0x7F0301F3 - public const int queryBackground = 2130903539; + public const int progressBarStyle = 2130903539; // aapt resource value: 0x7F0301F4 - public const int queryHint = 2130903540; + public const int queryBackground = 2130903540; // aapt resource value: 0x7F0301F5 - public const int radioButtonStyle = 2130903541; + public const int queryHint = 2130903541; // aapt resource value: 0x7F0301F6 - public const int rangeFillColor = 2130903542; + public const int radioButtonStyle = 2130903542; // aapt resource value: 0x7F0301F7 - public const int ratingBarStyle = 2130903543; + public const int rangeFillColor = 2130903543; // aapt resource value: 0x7F0301F8 - public const int ratingBarStyleIndicator = 2130903544; + public const int ratingBarStyle = 2130903544; // aapt resource value: 0x7F0301F9 - public const int ratingBarStyleSmall = 2130903545; + public const int ratingBarStyleIndicator = 2130903545; // aapt resource value: 0x7F0301FA - public const int recyclerViewStyle = 2130903546; + public const int ratingBarStyleSmall = 2130903546; // aapt resource value: 0x7F0301FB - public const int reverseLayout = 2130903547; + public const int recyclerViewStyle = 2130903547; // aapt resource value: 0x7F0301FC - public const int rippleColor = 2130903548; + public const int reverseLayout = 2130903548; // aapt resource value: 0x7F0301FD - public const int scopeUris = 2130903549; + public const int rippleColor = 2130903549; // aapt resource value: 0x7F0301FE - public const int scrimAnimationDuration = 2130903550; + public const int scopeUris = 2130903550; // aapt resource value: 0x7F0301FF - public const int scrimBackground = 2130903551; + public const int scrimAnimationDuration = 2130903551; // aapt resource value: 0x7F030200 - public const int scrimVisibleHeightTrigger = 2130903552; + public const int scrimBackground = 2130903552; // aapt resource value: 0x7F030201 - public const int scrollViewStyle = 2130903553; + public const int scrimVisibleHeightTrigger = 2130903553; // aapt resource value: 0x7F030202 - public const int searchHintIcon = 2130903554; + public const int scrollViewStyle = 2130903554; // aapt resource value: 0x7F030203 - public const int searchIcon = 2130903555; + public const int searchHintIcon = 2130903555; // aapt resource value: 0x7F030204 - public const int searchViewStyle = 2130903556; + public const int searchIcon = 2130903556; // aapt resource value: 0x7F030205 - public const int seekBarStyle = 2130903557; + public const int searchViewStyle = 2130903557; // aapt resource value: 0x7F030206 - public const int selectableItemBackground = 2130903558; + public const int seekBarStyle = 2130903558; // aapt resource value: 0x7F030207 - public const int selectableItemBackgroundBorderless = 2130903559; + public const int selectableItemBackground = 2130903559; // aapt resource value: 0x7F030208 - public const int selectionRequired = 2130903560; + public const int selectableItemBackgroundBorderless = 2130903560; // aapt resource value: 0x7F030209 - public const int shapeAppearance = 2130903561; + public const int selectionRequired = 2130903561; // aapt resource value: 0x7F03020A - public const int shapeAppearanceLargeComponent = 2130903562; + public const int shapeAppearance = 2130903562; // aapt resource value: 0x7F03020B - public const int shapeAppearanceMediumComponent = 2130903563; + public const int shapeAppearanceLargeComponent = 2130903563; // aapt resource value: 0x7F03020C - public const int shapeAppearanceOverlay = 2130903564; + public const int shapeAppearanceMediumComponent = 2130903564; // aapt resource value: 0x7F03020D - public const int shapeAppearanceSmallComponent = 2130903565; + public const int shapeAppearanceOverlay = 2130903565; // aapt resource value: 0x7F03020E - public const int showAsAction = 2130903566; + public const int shapeAppearanceSmallComponent = 2130903566; // aapt resource value: 0x7F03020F - public const int showDividers = 2130903567; + public const int showAsAction = 2130903567; // aapt resource value: 0x7F030210 - public const int showMotionSpec = 2130903568; + public const int showDividers = 2130903568; // aapt resource value: 0x7F030211 - public const int showText = 2130903569; + public const int showMotionSpec = 2130903569; // aapt resource value: 0x7F030212 - public const int showTitle = 2130903570; + public const int showText = 2130903570; // aapt resource value: 0x7F030213 - public const int shrinkMotionSpec = 2130903571; + public const int showTitle = 2130903571; // aapt resource value: 0x7F030214 - public const int singleChoiceItemLayout = 2130903572; + public const int shrinkMotionSpec = 2130903572; // aapt resource value: 0x7F030215 - public const int singleLine = 2130903573; + public const int singleChoiceItemLayout = 2130903573; // aapt resource value: 0x7F030216 - public const int singleSelection = 2130903574; + public const int singleLine = 2130903574; // aapt resource value: 0x7F030217 - public const int sliderStyle = 2130903575; + public const int singleSelection = 2130903575; // aapt resource value: 0x7F030218 - public const int snackbarButtonStyle = 2130903576; + public const int sliderStyle = 2130903576; // aapt resource value: 0x7F030219 - public const int snackbarStyle = 2130903577; + public const int snackbarButtonStyle = 2130903577; // aapt resource value: 0x7F03021A - public const int snackbarTextViewStyle = 2130903578; + public const int snackbarStyle = 2130903578; // aapt resource value: 0x7F03021B - public const int spanCount = 2130903579; + public const int snackbarTextViewStyle = 2130903579; // aapt resource value: 0x7F03021C - public const int spinBars = 2130903580; + public const int spanCount = 2130903580; // aapt resource value: 0x7F03021D - public const int spinnerDropDownItemStyle = 2130903581; + public const int spinBars = 2130903581; // aapt resource value: 0x7F03021E - public const int spinnerStyle = 2130903582; + public const int spinnerDropDownItemStyle = 2130903582; // aapt resource value: 0x7F03021F - public const int splitTrack = 2130903583; + public const int spinnerStyle = 2130903583; // aapt resource value: 0x7F030220 - public const int srcCompat = 2130903584; + public const int splitTrack = 2130903584; // aapt resource value: 0x7F030221 - public const int stackFromEnd = 2130903585; + public const int srcCompat = 2130903585; // aapt resource value: 0x7F030222 - public const int startIconCheckable = 2130903586; + public const int stackFromEnd = 2130903586; // aapt resource value: 0x7F030223 - public const int startIconContentDescription = 2130903587; + public const int startIconCheckable = 2130903587; // aapt resource value: 0x7F030224 - public const int startIconDrawable = 2130903588; + public const int startIconContentDescription = 2130903588; // aapt resource value: 0x7F030225 - public const int startIconTint = 2130903589; + public const int startIconDrawable = 2130903589; // aapt resource value: 0x7F030226 - public const int startIconTintMode = 2130903590; + public const int startIconTint = 2130903590; // aapt resource value: 0x7F030227 - public const int state_above_anchor = 2130903591; + public const int startIconTintMode = 2130903591; // aapt resource value: 0x7F030228 - public const int state_collapsed = 2130903592; + public const int state_above_anchor = 2130903592; // aapt resource value: 0x7F030229 - public const int state_collapsible = 2130903593; + public const int state_collapsed = 2130903593; // aapt resource value: 0x7F03022A - public const int state_dragged = 2130903594; + public const int state_collapsible = 2130903594; // aapt resource value: 0x7F03022B - public const int state_liftable = 2130903595; + public const int state_dragged = 2130903595; // aapt resource value: 0x7F03022C - public const int state_lifted = 2130903596; + public const int state_liftable = 2130903596; // aapt resource value: 0x7F03022D - public const int statusBarBackground = 2130903597; + public const int state_lifted = 2130903597; // aapt resource value: 0x7F03022E - public const int statusBarForeground = 2130903598; + public const int statusBarBackground = 2130903598; // aapt resource value: 0x7F03022F - public const int statusBarScrim = 2130903599; + public const int statusBarForeground = 2130903599; // aapt resource value: 0x7F030230 - public const int strokeColor = 2130903600; + public const int statusBarScrim = 2130903600; // aapt resource value: 0x7F030231 - public const int strokeWidth = 2130903601; + public const int strokeColor = 2130903601; // aapt resource value: 0x7F030232 - public const int subMenuArrow = 2130903602; + public const int strokeWidth = 2130903602; // aapt resource value: 0x7F030233 - public const int submitBackground = 2130903603; + public const int subMenuArrow = 2130903603; // aapt resource value: 0x7F030234 - public const int subtitle = 2130903604; + public const int submitBackground = 2130903604; // aapt resource value: 0x7F030235 - public const int subtitleTextAppearance = 2130903605; + public const int subtitle = 2130903605; // aapt resource value: 0x7F030236 - public const int subtitleTextColor = 2130903606; + public const int subtitleTextAppearance = 2130903606; // aapt resource value: 0x7F030237 - public const int subtitleTextStyle = 2130903607; + public const int subtitleTextColor = 2130903607; // aapt resource value: 0x7F030238 - public const int suffixText = 2130903608; + public const int subtitleTextStyle = 2130903608; // aapt resource value: 0x7F030239 - public const int suffixTextAppearance = 2130903609; + public const int suffixText = 2130903609; // aapt resource value: 0x7F03023A - public const int suffixTextColor = 2130903610; + public const int suffixTextAppearance = 2130903610; // aapt resource value: 0x7F03023B - public const int suggestionRowLayout = 2130903611; + public const int suffixTextColor = 2130903611; // aapt resource value: 0x7F03023C - public const int swipeRefreshLayoutProgressSpinnerBackgroundColor = 2130903612; + public const int suggestionRowLayout = 2130903612; // aapt resource value: 0x7F03023D - public const int switchMinWidth = 2130903613; + public const int swipeRefreshLayoutProgressSpinnerBackgroundColor = 2130903613; // aapt resource value: 0x7F03023E - public const int switchPadding = 2130903614; + public const int switchMinWidth = 2130903614; // aapt resource value: 0x7F03023F - public const int switchStyle = 2130903615; + public const int switchPadding = 2130903615; // aapt resource value: 0x7F030240 - public const int switchTextAppearance = 2130903616; + public const int switchStyle = 2130903616; // aapt resource value: 0x7F030241 - public const int tabBackground = 2130903617; + public const int switchTextAppearance = 2130903617; // aapt resource value: 0x7F030242 - public const int tabContentStart = 2130903618; + public const int tabBackground = 2130903618; // aapt resource value: 0x7F030243 - public const int tabGravity = 2130903619; + public const int tabContentStart = 2130903619; // aapt resource value: 0x7F030244 - public const int tabIconTint = 2130903620; + public const int tabGravity = 2130903620; // aapt resource value: 0x7F030245 - public const int tabIconTintMode = 2130903621; + public const int tabIconTint = 2130903621; // aapt resource value: 0x7F030246 - public const int tabIndicator = 2130903622; + public const int tabIconTintMode = 2130903622; // aapt resource value: 0x7F030247 - public const int tabIndicatorAnimationDuration = 2130903623; + public const int tabIndicator = 2130903623; // aapt resource value: 0x7F030248 - public const int tabIndicatorColor = 2130903624; + public const int tabIndicatorAnimationDuration = 2130903624; // aapt resource value: 0x7F030249 - public const int tabIndicatorFullWidth = 2130903625; + public const int tabIndicatorColor = 2130903625; // aapt resource value: 0x7F03024A - public const int tabIndicatorGravity = 2130903626; + public const int tabIndicatorFullWidth = 2130903626; // aapt resource value: 0x7F03024B - public const int tabIndicatorHeight = 2130903627; + public const int tabIndicatorGravity = 2130903627; // aapt resource value: 0x7F03024C - public const int tabInlineLabel = 2130903628; + public const int tabIndicatorHeight = 2130903628; // aapt resource value: 0x7F03024D - public const int tabMaxWidth = 2130903629; + public const int tabInlineLabel = 2130903629; // aapt resource value: 0x7F03024E - public const int tabMinWidth = 2130903630; + public const int tabMaxWidth = 2130903630; // aapt resource value: 0x7F03024F - public const int tabMode = 2130903631; + public const int tabMinWidth = 2130903631; // aapt resource value: 0x7F030250 - public const int tabPadding = 2130903632; + public const int tabMode = 2130903632; // aapt resource value: 0x7F030251 - public const int tabPaddingBottom = 2130903633; + public const int tabPadding = 2130903633; // aapt resource value: 0x7F030252 - public const int tabPaddingEnd = 2130903634; + public const int tabPaddingBottom = 2130903634; // aapt resource value: 0x7F030253 - public const int tabPaddingStart = 2130903635; + public const int tabPaddingEnd = 2130903635; // aapt resource value: 0x7F030254 - public const int tabPaddingTop = 2130903636; + public const int tabPaddingStart = 2130903636; // aapt resource value: 0x7F030255 - public const int tabRippleColor = 2130903637; + public const int tabPaddingTop = 2130903637; // aapt resource value: 0x7F030256 - public const int tabSelectedTextColor = 2130903638; + public const int tabRippleColor = 2130903638; // aapt resource value: 0x7F030257 - public const int tabStyle = 2130903639; + public const int tabSelectedTextColor = 2130903639; // aapt resource value: 0x7F030258 - public const int tabTextAppearance = 2130903640; + public const int tabStyle = 2130903640; // aapt resource value: 0x7F030259 - public const int tabTextColor = 2130903641; + public const int tabTextAppearance = 2130903641; // aapt resource value: 0x7F03025A - public const int tabUnboundedRipple = 2130903642; + public const int tabTextColor = 2130903642; // aapt resource value: 0x7F03025B - public const int textAllCaps = 2130903643; + public const int tabUnboundedRipple = 2130903643; // aapt resource value: 0x7F03025C - public const int textAppearanceBody1 = 2130903644; + public const int textAllCaps = 2130903644; // aapt resource value: 0x7F03025D - public const int textAppearanceBody2 = 2130903645; + public const int textAppearanceBody1 = 2130903645; // aapt resource value: 0x7F03025E - public const int textAppearanceButton = 2130903646; + public const int textAppearanceBody2 = 2130903646; // aapt resource value: 0x7F03025F - public const int textAppearanceCaption = 2130903647; + public const int textAppearanceButton = 2130903647; // aapt resource value: 0x7F030260 - public const int textAppearanceHeadline1 = 2130903648; + public const int textAppearanceCaption = 2130903648; // aapt resource value: 0x7F030261 - public const int textAppearanceHeadline2 = 2130903649; + public const int textAppearanceHeadline1 = 2130903649; // aapt resource value: 0x7F030262 - public const int textAppearanceHeadline3 = 2130903650; + public const int textAppearanceHeadline2 = 2130903650; // aapt resource value: 0x7F030263 - public const int textAppearanceHeadline4 = 2130903651; + public const int textAppearanceHeadline3 = 2130903651; // aapt resource value: 0x7F030264 - public const int textAppearanceHeadline5 = 2130903652; + public const int textAppearanceHeadline4 = 2130903652; // aapt resource value: 0x7F030265 - public const int textAppearanceHeadline6 = 2130903653; + public const int textAppearanceHeadline5 = 2130903653; // aapt resource value: 0x7F030266 - public const int textAppearanceLargePopupMenu = 2130903654; + public const int textAppearanceHeadline6 = 2130903654; // aapt resource value: 0x7F030267 - public const int textAppearanceLineHeightEnabled = 2130903655; + public const int textAppearanceLargePopupMenu = 2130903655; // aapt resource value: 0x7F030268 - public const int textAppearanceListItem = 2130903656; + public const int textAppearanceLineHeightEnabled = 2130903656; // aapt resource value: 0x7F030269 - public const int textAppearanceListItemSecondary = 2130903657; + public const int textAppearanceListItem = 2130903657; // aapt resource value: 0x7F03026A - public const int textAppearanceListItemSmall = 2130903658; + public const int textAppearanceListItemSecondary = 2130903658; // aapt resource value: 0x7F03026B - public const int textAppearanceOverline = 2130903659; + public const int textAppearanceListItemSmall = 2130903659; // aapt resource value: 0x7F03026C - public const int textAppearancePopupMenuHeader = 2130903660; + public const int textAppearanceOverline = 2130903660; // aapt resource value: 0x7F03026D - public const int textAppearanceSearchResultSubtitle = 2130903661; + public const int textAppearancePopupMenuHeader = 2130903661; // aapt resource value: 0x7F03026E - public const int textAppearanceSearchResultTitle = 2130903662; + public const int textAppearanceSearchResultSubtitle = 2130903662; // aapt resource value: 0x7F03026F - public const int textAppearanceSmallPopupMenu = 2130903663; + public const int textAppearanceSearchResultTitle = 2130903663; // aapt resource value: 0x7F030270 - public const int textAppearanceSubtitle1 = 2130903664; + public const int textAppearanceSmallPopupMenu = 2130903664; // aapt resource value: 0x7F030271 - public const int textAppearanceSubtitle2 = 2130903665; + public const int textAppearanceSubtitle1 = 2130903665; // aapt resource value: 0x7F030272 - public const int textColorAlertDialogListItem = 2130903666; + public const int textAppearanceSubtitle2 = 2130903666; // aapt resource value: 0x7F030273 - public const int textColorSearchUrl = 2130903667; + public const int textColorAlertDialogListItem = 2130903667; // aapt resource value: 0x7F030274 - public const int textEndPadding = 2130903668; + public const int textColorSearchUrl = 2130903668; // aapt resource value: 0x7F030275 - public const int textInputLayoutFocusedRectEnabled = 2130903669; + public const int textEndPadding = 2130903669; // aapt resource value: 0x7F030276 - public const int textInputStyle = 2130903670; + public const int textInputLayoutFocusedRectEnabled = 2130903670; // aapt resource value: 0x7F030277 - public const int textLocale = 2130903671; + public const int textInputStyle = 2130903671; // aapt resource value: 0x7F030278 - public const int textStartPadding = 2130903672; + public const int textLocale = 2130903672; // aapt resource value: 0x7F030279 - public const int theme = 2130903673; + public const int textStartPadding = 2130903673; // aapt resource value: 0x7F03027A - public const int themeLineHeight = 2130903674; + public const int theme = 2130903674; // aapt resource value: 0x7F03027B - public const int thickness = 2130903675; + public const int themeLineHeight = 2130903675; // aapt resource value: 0x7F03027C - public const int thumbColor = 2130903676; + public const int thickness = 2130903676; // aapt resource value: 0x7F03027D - public const int thumbElevation = 2130903677; + public const int thumbColor = 2130903677; // aapt resource value: 0x7F03027E - public const int thumbRadius = 2130903678; + public const int thumbElevation = 2130903678; // aapt resource value: 0x7F03027F - public const int thumbTextPadding = 2130903679; + public const int thumbRadius = 2130903679; // aapt resource value: 0x7F030280 - public const int thumbTint = 2130903680; + public const int thumbTextPadding = 2130903680; // aapt resource value: 0x7F030281 - public const int thumbTintMode = 2130903681; + public const int thumbTint = 2130903681; // aapt resource value: 0x7F030282 - public const int tickColor = 2130903682; + public const int thumbTintMode = 2130903682; // aapt resource value: 0x7F030283 - public const int tickColorActive = 2130903683; + public const int tickColor = 2130903683; // aapt resource value: 0x7F030284 - public const int tickColorInactive = 2130903684; + public const int tickColorActive = 2130903684; // aapt resource value: 0x7F030285 - public const int tickMark = 2130903685; + public const int tickColorInactive = 2130903685; // aapt resource value: 0x7F030286 - public const int tickMarkTint = 2130903686; + public const int tickMark = 2130903686; // aapt resource value: 0x7F030287 - public const int tickMarkTintMode = 2130903687; + public const int tickMarkTint = 2130903687; // aapt resource value: 0x7F030288 - public const int tint = 2130903688; + public const int tickMarkTintMode = 2130903688; // aapt resource value: 0x7F030289 - public const int tintMode = 2130903689; + public const int tint = 2130903689; // aapt resource value: 0x7F03028A - public const int title = 2130903690; + public const int tintMode = 2130903690; // aapt resource value: 0x7F03028B - public const int titleEnabled = 2130903691; + public const int title = 2130903691; // aapt resource value: 0x7F03028C - public const int titleMargin = 2130903692; + public const int titleEnabled = 2130903692; // aapt resource value: 0x7F03028D - public const int titleMarginBottom = 2130903693; + public const int titleMargin = 2130903693; // aapt resource value: 0x7F03028E - public const int titleMarginEnd = 2130903694; - - // aapt resource value: 0x7F030291 - public const int titleMargins = 2130903697; + public const int titleMarginBottom = 2130903694; // aapt resource value: 0x7F03028F - public const int titleMarginStart = 2130903695; - - // aapt resource value: 0x7F030290 - public const int titleMarginTop = 2130903696; + public const int titleMarginEnd = 2130903695; // aapt resource value: 0x7F030292 - public const int titleTextAppearance = 2130903698; + public const int titleMargins = 2130903698; + + // aapt resource value: 0x7F030290 + public const int titleMarginStart = 2130903696; + + // aapt resource value: 0x7F030291 + public const int titleMarginTop = 2130903697; // aapt resource value: 0x7F030293 - public const int titleTextColor = 2130903699; + public const int titleTextAppearance = 2130903699; // aapt resource value: 0x7F030294 - public const int titleTextStyle = 2130903700; + public const int titleTextColor = 2130903700; // aapt resource value: 0x7F030295 - public const int toolbarId = 2130903701; + public const int titleTextStyle = 2130903701; // aapt resource value: 0x7F030296 - public const int toolbarNavigationButtonStyle = 2130903702; + public const int toolbarId = 2130903702; // aapt resource value: 0x7F030297 - public const int toolbarStyle = 2130903703; + public const int toolbarNavigationButtonStyle = 2130903703; // aapt resource value: 0x7F030298 - public const int tooltipForegroundColor = 2130903704; + public const int toolbarStyle = 2130903704; // aapt resource value: 0x7F030299 - public const int tooltipFrameBackground = 2130903705; + public const int tooltipForegroundColor = 2130903705; // aapt resource value: 0x7F03029A - public const int tooltipStyle = 2130903706; + public const int tooltipFrameBackground = 2130903706; // aapt resource value: 0x7F03029B - public const int tooltipText = 2130903707; + public const int tooltipStyle = 2130903707; // aapt resource value: 0x7F03029C - public const int track = 2130903708; + public const int tooltipText = 2130903708; // aapt resource value: 0x7F03029D - public const int trackColor = 2130903709; + public const int track = 2130903709; // aapt resource value: 0x7F03029E - public const int trackColorActive = 2130903710; + public const int trackColor = 2130903710; // aapt resource value: 0x7F03029F - public const int trackColorInactive = 2130903711; + public const int trackColorActive = 2130903711; // aapt resource value: 0x7F0302A0 - public const int trackHeight = 2130903712; + public const int trackColorInactive = 2130903712; // aapt resource value: 0x7F0302A1 - public const int trackTint = 2130903713; + public const int trackHeight = 2130903713; // aapt resource value: 0x7F0302A2 - public const int trackTintMode = 2130903714; + public const int trackTint = 2130903714; // aapt resource value: 0x7F0302A3 - public const int transitionShapeAppearance = 2130903715; + public const int trackTintMode = 2130903715; // aapt resource value: 0x7F0302A4 - public const int ttcIndex = 2130903716; + public const int transitionShapeAppearance = 2130903716; // aapt resource value: 0x7F0302A5 - public const int uiCompass = 2130903717; + public const int ttcIndex = 2130903717; // aapt resource value: 0x7F0302A6 - public const int uiMapToolbar = 2130903718; + public const int uiCompass = 2130903718; // aapt resource value: 0x7F0302A7 - public const int uiRotateGestures = 2130903719; + public const int uiMapToolbar = 2130903719; // aapt resource value: 0x7F0302A8 - public const int uiScrollGestures = 2130903720; + public const int uiRotateGestures = 2130903720; // aapt resource value: 0x7F0302A9 - public const int uiScrollGesturesDuringRotateOrZoom = 2130903721; + public const int uiScrollGestures = 2130903721; // aapt resource value: 0x7F0302AA - public const int uiTiltGestures = 2130903722; + public const int uiScrollGesturesDuringRotateOrZoom = 2130903722; // aapt resource value: 0x7F0302AB - public const int uiZoomControls = 2130903723; + public const int uiTiltGestures = 2130903723; // aapt resource value: 0x7F0302AC - public const int uiZoomGestures = 2130903724; + public const int uiZoomControls = 2130903724; // aapt resource value: 0x7F0302AD - public const int useCompatPadding = 2130903725; + public const int uiZoomGestures = 2130903725; // aapt resource value: 0x7F0302AE - public const int useMaterialThemeColors = 2130903726; + public const int useCompatPadding = 2130903726; // aapt resource value: 0x7F0302AF - public const int useViewLifecycle = 2130903727; + public const int useMaterialThemeColors = 2130903727; // aapt resource value: 0x7F0302B0 - public const int values = 2130903728; + public const int useViewLifecycle = 2130903728; // aapt resource value: 0x7F0302B1 - public const int verticalOffset = 2130903729; + public const int values = 2130903729; // aapt resource value: 0x7F0302B2 - public const int viewInflaterClass = 2130903730; + public const int verticalOffset = 2130903730; // aapt resource value: 0x7F0302B3 - public const int voiceIcon = 2130903731; + public const int viewInflaterClass = 2130903731; // aapt resource value: 0x7F0302B4 - public const int windowActionBar = 2130903732; + public const int voiceIcon = 2130903732; // aapt resource value: 0x7F0302B5 - public const int windowActionBarOverlay = 2130903733; + public const int windowActionBar = 2130903733; // aapt resource value: 0x7F0302B6 - public const int windowActionModeOverlay = 2130903734; + public const int windowActionBarOverlay = 2130903734; // aapt resource value: 0x7F0302B7 - public const int windowFixedHeightMajor = 2130903735; + public const int windowActionModeOverlay = 2130903735; // aapt resource value: 0x7F0302B8 - public const int windowFixedHeightMinor = 2130903736; + public const int windowFixedHeightMajor = 2130903736; // aapt resource value: 0x7F0302B9 - public const int windowFixedWidthMajor = 2130903737; + public const int windowFixedHeightMinor = 2130903737; // aapt resource value: 0x7F0302BA - public const int windowFixedWidthMinor = 2130903738; + public const int windowFixedWidthMajor = 2130903738; // aapt resource value: 0x7F0302BB - public const int windowMinWidthMajor = 2130903739; + public const int windowFixedWidthMinor = 2130903739; // aapt resource value: 0x7F0302BC - public const int windowMinWidthMinor = 2130903740; + public const int windowMinWidthMajor = 2130903740; // aapt resource value: 0x7F0302BD - public const int windowNoTitle = 2130903741; + public const int windowMinWidthMinor = 2130903741; // aapt resource value: 0x7F0302BE - public const int yearSelectedStyle = 2130903742; + public const int windowNoTitle = 2130903742; // aapt resource value: 0x7F0302BF - public const int yearStyle = 2130903743; + public const int yearSelectedStyle = 2130903743; // aapt resource value: 0x7F0302C0 - public const int yearTodayStyle = 2130903744; + public const int yearStyle = 2130903744; // aapt resource value: 0x7F0302C1 - public const int zOrderOnTop = 2130903745; + public const int yearTodayStyle = 2130903745; + + // aapt resource value: 0x7F0302C2 + public const int zOrderOnTop = 2130903746; static Attribute() { @@ -17541,142 +17526,160 @@ namespace TINK.Droid public const int spacer = 2131231035; // aapt resource value: 0x7F08013C - public const int split_action_bar = 2131231036; + public const int special_effects_controller_view_tag = 2131231036; // aapt resource value: 0x7F08013D - public const int src_atop = 2131231037; + public const int split_action_bar = 2131231037; // aapt resource value: 0x7F08013E - public const int src_in = 2131231038; + public const int src_atop = 2131231038; // aapt resource value: 0x7F08013F - public const int src_over = 2131231039; + public const int src_in = 2131231039; // aapt resource value: 0x7F080140 - public const int standard = 2131231040; + public const int src_over = 2131231040; // aapt resource value: 0x7F080141 - public const int start = 2131231041; + public const int standard = 2131231041; // aapt resource value: 0x7F080142 - public const int status_bar_latest_event_content = 2131231042; + public const int start = 2131231042; // aapt resource value: 0x7F080143 - public const int stretch = 2131231043; + public const int status_bar_latest_event_content = 2131231043; // aapt resource value: 0x7F080144 - public const int submenuarrow = 2131231044; + public const int stretch = 2131231044; // aapt resource value: 0x7F080145 - public const int submit_area = 2131231045; + public const int submenuarrow = 2131231045; + + // aapt resource value: 0x7F080146 + public const int submit_area = 2131231046; // aapt resource value: 0x7F080007 public const int SYM = 2131230727; - // aapt resource value: 0x7F080146 - public const int tabMode = 2131231046; - // aapt resource value: 0x7F080147 - public const int tag_accessibility_actions = 2131231047; + public const int tabMode = 2131231047; // aapt resource value: 0x7F080148 - public const int tag_accessibility_clickable_spans = 2131231048; + public const int tag_accessibility_actions = 2131231048; // aapt resource value: 0x7F080149 - public const int tag_accessibility_heading = 2131231049; + public const int tag_accessibility_clickable_spans = 2131231049; // aapt resource value: 0x7F08014A - public const int tag_accessibility_pane_title = 2131231050; + public const int tag_accessibility_heading = 2131231050; // aapt resource value: 0x7F08014B - public const int tag_screen_reader_focusable = 2131231051; + public const int tag_accessibility_pane_title = 2131231051; // aapt resource value: 0x7F08014C - public const int tag_transition_group = 2131231052; + public const int tag_on_apply_window_listener = 2131231052; // aapt resource value: 0x7F08014D - public const int tag_unhandled_key_event_manager = 2131231053; + public const int tag_on_receive_content_listener = 2131231053; // aapt resource value: 0x7F08014E - public const int tag_unhandled_key_listeners = 2131231054; + public const int tag_on_receive_content_mime_types = 2131231054; // aapt resource value: 0x7F08014F - public const int terrain = 2131231055; + public const int tag_screen_reader_focusable = 2131231055; // aapt resource value: 0x7F080150 - public const int test_checkbox_android_button_tint = 2131231056; + public const int tag_state_description = 2131231056; // aapt resource value: 0x7F080151 - public const int test_checkbox_app_button_tint = 2131231057; + public const int tag_transition_group = 2131231057; // aapt resource value: 0x7F080152 - public const int test_radiobutton_android_button_tint = 2131231058; + public const int tag_unhandled_key_event_manager = 2131231058; // aapt resource value: 0x7F080153 - public const int test_radiobutton_app_button_tint = 2131231059; + public const int tag_unhandled_key_listeners = 2131231059; // aapt resource value: 0x7F080154 - public const int text = 2131231060; + public const int tag_window_insets_animation_callback = 2131231060; // aapt resource value: 0x7F080155 - public const int text2 = 2131231061; + public const int terrain = 2131231061; // aapt resource value: 0x7F080156 - public const int textEnd = 2131231062; - - // aapt resource value: 0x7F08015C - public const int textinput_counter = 2131231068; - - // aapt resource value: 0x7F08015D - public const int textinput_error = 2131231069; - - // aapt resource value: 0x7F08015E - public const int textinput_helper_text = 2131231070; - - // aapt resource value: 0x7F08015F - public const int textinput_placeholder = 2131231071; - - // aapt resource value: 0x7F080160 - public const int textinput_prefix_text = 2131231072; - - // aapt resource value: 0x7F080161 - public const int textinput_suffix_text = 2131231073; + public const int test_checkbox_android_button_tint = 2131231062; // aapt resource value: 0x7F080157 - public const int textSpacerNoButtons = 2131231063; + public const int test_checkbox_app_button_tint = 2131231063; // aapt resource value: 0x7F080158 - public const int textSpacerNoTitle = 2131231064; + public const int test_radiobutton_android_button_tint = 2131231064; // aapt resource value: 0x7F080159 - public const int textStart = 2131231065; + public const int test_radiobutton_app_button_tint = 2131231065; // aapt resource value: 0x7F08015A - public const int text_input_end_icon = 2131231066; + public const int text = 2131231066; // aapt resource value: 0x7F08015B - public const int text_input_start_icon = 2131231067; + public const int text2 = 2131231067; + + // aapt resource value: 0x7F08015C + public const int textEnd = 2131231068; // aapt resource value: 0x7F080162 - public const int time = 2131231074; + public const int textinput_counter = 2131231074; // aapt resource value: 0x7F080163 - public const int title = 2131231075; + public const int textinput_error = 2131231075; // aapt resource value: 0x7F080164 - public const int titleDividerNoCustom = 2131231076; + public const int textinput_helper_text = 2131231076; // aapt resource value: 0x7F080165 - public const int title_template = 2131231077; + public const int textinput_placeholder = 2131231077; // aapt resource value: 0x7F080166 - public const int toolbar = 2131231078; + public const int textinput_prefix_text = 2131231078; // aapt resource value: 0x7F080167 - public const int top = 2131231079; + public const int textinput_suffix_text = 2131231079; + + // aapt resource value: 0x7F08015D + public const int textSpacerNoButtons = 2131231069; + + // aapt resource value: 0x7F08015E + public const int textSpacerNoTitle = 2131231070; + + // aapt resource value: 0x7F08015F + public const int textStart = 2131231071; + + // aapt resource value: 0x7F080160 + public const int text_input_end_icon = 2131231072; + + // aapt resource value: 0x7F080161 + public const int text_input_start_icon = 2131231073; // aapt resource value: 0x7F080168 - public const int topPanel = 2131231080; + public const int time = 2131231080; + + // aapt resource value: 0x7F080169 + public const int title = 2131231081; + + // aapt resource value: 0x7F08016A + public const int titleDividerNoCustom = 2131231082; + + // aapt resource value: 0x7F08016B + public const int title_template = 2131231083; + + // aapt resource value: 0x7F08016C + public const int toolbar = 2131231084; + + // aapt resource value: 0x7F08016D + public const int top = 2131231085; + + // aapt resource value: 0x7F08016E + public const int topPanel = 2131231086; // aapt resource value: 0x7F080008 public const int TOP_END = 2131230728; @@ -17684,71 +17687,77 @@ namespace TINK.Droid // aapt resource value: 0x7F080009 public const int TOP_START = 2131230729; - // aapt resource value: 0x7F080169 - public const int touch_outside = 2131231081; - - // aapt resource value: 0x7F08016A - public const int transition_current_scene = 2131231082; - - // aapt resource value: 0x7F08016B - public const int transition_layout_save = 2131231083; - - // aapt resource value: 0x7F08016C - public const int transition_position = 2131231084; - - // aapt resource value: 0x7F08016D - public const int transition_scene_layoutid_cache = 2131231085; - - // aapt resource value: 0x7F08016E - public const int transition_transform = 2131231086; - // aapt resource value: 0x7F08016F - public const int @unchecked = 2131231087; + public const int touch_outside = 2131231087; // aapt resource value: 0x7F080170 - public const int uniform = 2131231088; + public const int transition_current_scene = 2131231088; // aapt resource value: 0x7F080171 - public const int unlabeled = 2131231089; + public const int transition_layout_save = 2131231089; // aapt resource value: 0x7F080172 - public const int up = 2131231090; + public const int transition_position = 2131231090; // aapt resource value: 0x7F080173 - public const int useLogo = 2131231091; + public const int transition_scene_layoutid_cache = 2131231091; // aapt resource value: 0x7F080174 - public const int view_offset_helper = 2131231092; + public const int transition_transform = 2131231092; // aapt resource value: 0x7F080175 - public const int view_tree_saved_state_registry_owner = 2131231093; + public const int @unchecked = 2131231093; // aapt resource value: 0x7F080176 - public const int visible = 2131231094; + public const int uniform = 2131231094; // aapt resource value: 0x7F080177 - public const int visible_removing_fragment_view_tag = 2131231095; + public const int unlabeled = 2131231095; // aapt resource value: 0x7F080178 - public const int volume_item_container = 2131231096; + public const int up = 2131231096; // aapt resource value: 0x7F080179 - public const int webview = 2131231097; + public const int useLogo = 2131231097; // aapt resource value: 0x7F08017A - public const int wide = 2131231098; - - // aapt resource value: 0x7F08017C - public const int withinBounds = 2131231100; + public const int view_offset_helper = 2131231098; // aapt resource value: 0x7F08017B - public const int withText = 2131231099; + public const int view_tree_lifecycle_owner = 2131231099; + + // aapt resource value: 0x7F08017C + public const int view_tree_saved_state_registry_owner = 2131231100; // aapt resource value: 0x7F08017D - public const int wrap_content = 2131231101; + public const int view_tree_view_model_store_owner = 2131231101; // aapt resource value: 0x7F08017E - public const int zero_corner_chip = 2131231102; + public const int visible = 2131231102; + + // aapt resource value: 0x7F08017F + public const int visible_removing_fragment_view_tag = 2131231103; + + // aapt resource value: 0x7F080180 + public const int volume_item_container = 2131231104; + + // aapt resource value: 0x7F080181 + public const int webview = 2131231105; + + // aapt resource value: 0x7F080182 + public const int wide = 2131231106; + + // aapt resource value: 0x7F080184 + public const int withinBounds = 2131231108; + + // aapt resource value: 0x7F080183 + public const int withText = 2131231107; + + // aapt resource value: 0x7F080185 + public const int wrap_content = 2131231109; + + // aapt resource value: 0x7F080186 + public const int zero_corner_chip = 2131231110; static Id() { @@ -20928,7 +20937,7 @@ namespace TINK.Droid public partial class Styleable { - // aapt resource value: { 0x7F030035,0x7F03003C,0x7F03003D,0x7F0300C0,0x7F0300C1,0x7F0300C2,0x7F0300C3,0x7F0300C4,0x7F0300C5,0x7F0300DF,0x7F0300E8,0x7F0300E9,0x7F0300FD,0x7F03013D,0x7F030143,0x7F030149,0x7F03014A,0x7F03014D,0x7F030159,0x7F030166,0x7F03019F,0x7F0301D1,0x7F0301EA,0x7F0301F1,0x7F0301F2,0x7F030234,0x7F030237,0x7F03028A,0x7F030294 } + // aapt resource value: { 0x7F030035,0x7F03003C,0x7F03003D,0x7F0300C0,0x7F0300C1,0x7F0300C2,0x7F0300C3,0x7F0300C4,0x7F0300C5,0x7F0300DF,0x7F0300E8,0x7F0300E9,0x7F0300FD,0x7F03013E,0x7F030144,0x7F03014A,0x7F03014B,0x7F03014E,0x7F03015A,0x7F030167,0x7F0301A0,0x7F0301D2,0x7F0301EB,0x7F0301F2,0x7F0301F3,0x7F030235,0x7F030238,0x7F03028B,0x7F030295 } public static int[] ActionBar = new int[] { 2130903093, 2130903100, @@ -20943,22 +20952,22 @@ namespace TINK.Droid 2130903272, 2130903273, 2130903293, - 2130903357, - 2130903363, - 2130903369, + 2130903358, + 2130903364, 2130903370, - 2130903373, - 2130903385, - 2130903398, - 2130903455, - 2130903505, - 2130903530, - 2130903537, + 2130903371, + 2130903374, + 2130903386, + 2130903399, + 2130903456, + 2130903506, + 2130903531, 2130903538, - 2130903604, - 2130903607, - 2130903690, - 2130903700}; + 2130903539, + 2130903605, + 2130903608, + 2130903691, + 2130903701}; // aapt resource value: { 0x10100B3 } public static int[] ActionBarLayout = new int[] { @@ -21065,14 +21074,14 @@ namespace TINK.Droid public static int[] ActionMenuView = new int[] { -1}; - // aapt resource value: { 0x7F030035,0x7F03003C,0x7F0300A1,0x7F03013D,0x7F030237,0x7F030294 } + // aapt resource value: { 0x7F030035,0x7F03003C,0x7F0300A1,0x7F03013E,0x7F030238,0x7F030295 } public static int[] ActionMode = new int[] { 2130903093, 2130903100, 2130903201, - 2130903357, - 2130903607, - 2130903700}; + 2130903358, + 2130903608, + 2130903701}; // aapt resource value: 0 public const int ActionMode_background = 0; @@ -21092,10 +21101,10 @@ namespace TINK.Droid // aapt resource value: 5 public const int ActionMode_titleTextStyle = 5; - // aapt resource value: { 0x7F030110,0x7F03015A } + // aapt resource value: { 0x7F030110,0x7F03015B } public static int[] ActivityChooserView = new int[] { 2130903312, - 2130903386}; + 2130903387}; // aapt resource value: 0 public const int ActivityChooserView_expandActivityOverflowButtonDrawable = 0; @@ -21103,16 +21112,16 @@ namespace TINK.Droid // aapt resource value: 1 public const int ActivityChooserView_initialActivityCount = 1; - // aapt resource value: { 0x10100F2,0x7F030067,0x7F030068,0x7F030193,0x7F030194,0x7F0301CE,0x7F030212,0x7F030214 } + // aapt resource value: { 0x10100F2,0x7F030067,0x7F030068,0x7F030194,0x7F030195,0x7F0301CF,0x7F030213,0x7F030215 } public static int[] AlertDialog = new int[] { 16842994, 2130903143, 2130903144, - 2130903443, 2130903444, - 2130903502, - 2130903570, - 2130903572}; + 2130903445, + 2130903503, + 2130903571, + 2130903573}; // aapt resource value: 0 public const int AlertDialog_android_layout = 0; @@ -21195,23 +21204,23 @@ namespace TINK.Droid // aapt resource value: 1 public const int AnimatedStateListDrawableTransition_android_toId = 1; - // aapt resource value: { 0x10100D4,0x101048F,0x1010540,0x7F0300FD,0x7F030111,0x7F03018B,0x7F03018C,0x7F03022E } + // aapt resource value: { 0x10100D4,0x101048F,0x1010540,0x7F0300FD,0x7F030111,0x7F03018C,0x7F03018D,0x7F03022F } public static int[] AppBarLayout = new int[] { 16842964, 16843919, 16844096, 2130903293, 2130903313, - 2130903435, 2130903436, - 2130903598}; + 2130903437, + 2130903599}; - // aapt resource value: { 0x7F030228,0x7F030229,0x7F03022B,0x7F03022C } + // aapt resource value: { 0x7F030229,0x7F03022A,0x7F03022C,0x7F03022D } public static int[] AppBarLayoutStates = new int[] { - 2130903592, 2130903593, - 2130903595, - 2130903596}; + 2130903594, + 2130903596, + 2130903597}; // aapt resource value: 0 public const int AppBarLayoutStates_state_collapsed = 0; @@ -21240,10 +21249,10 @@ namespace TINK.Droid // aapt resource value: 4 public const int AppBarLayout_expanded = 4; - // aapt resource value: { 0x7F030189,0x7F03018A } + // aapt resource value: { 0x7F03018A,0x7F03018B } public static int[] AppBarLayout_Layout = new int[] { - 2130903433, - 2130903434}; + 2130903434, + 2130903435}; // aapt resource value: 0 public const int AppBarLayout_Layout_layout_scrollFlags = 0; @@ -21260,12 +21269,12 @@ namespace TINK.Droid // aapt resource value: 7 public const int AppBarLayout_statusBarForeground = 7; - // aapt resource value: { 0x1010119,0x7F030220,0x7F030288,0x7F030289 } + // aapt resource value: { 0x1010119,0x7F030221,0x7F030289,0x7F03028A } public static int[] AppCompatImageView = new int[] { 16843033, - 2130903584, - 2130903688, - 2130903689}; + 2130903585, + 2130903689, + 2130903690}; // aapt resource value: 0 public const int AppCompatImageView_android_src = 0; @@ -21279,12 +21288,12 @@ namespace TINK.Droid // aapt resource value: 3 public const int AppCompatImageView_tintMode = 3; - // aapt resource value: { 0x1010142,0x7F030285,0x7F030286,0x7F030287 } + // aapt resource value: { 0x1010142,0x7F030286,0x7F030287,0x7F030288 } public static int[] AppCompatSeekBar = new int[] { 16843074, - 2130903685, 2130903686, - 2130903687}; + 2130903687, + 2130903688}; // aapt resource value: 0 public const int AppCompatSeekBar_android_thumb = 0; @@ -21329,7 +21338,7 @@ namespace TINK.Droid // aapt resource value: 0 public const int AppCompatTextHelper_android_textAppearance = 0; - // aapt resource value: { 0x1010034,0x7F030030,0x7F030031,0x7F030032,0x7F030033,0x7F030034,0x7F0300ED,0x7F0300EE,0x7F0300EF,0x7F0300F0,0x7F0300F2,0x7F0300F3,0x7F0300F4,0x7F0300F5,0x7F030129,0x7F03012C,0x7F030134,0x7F03017A,0x7F03018D,0x7F03025B,0x7F030277 } + // aapt resource value: { 0x1010034,0x7F030030,0x7F030031,0x7F030032,0x7F030033,0x7F030034,0x7F0300ED,0x7F0300EE,0x7F0300EF,0x7F0300F0,0x7F0300F2,0x7F0300F3,0x7F0300F4,0x7F0300F5,0x7F030129,0x7F03012C,0x7F030135,0x7F03017B,0x7F03018E,0x7F03025C,0x7F030278 } public static int[] AppCompatTextView = new int[] { 16842804, 2130903088, @@ -21347,11 +21356,11 @@ namespace TINK.Droid 2130903285, 2130903337, 2130903340, - 2130903348, - 2130903418, - 2130903437, - 2130903643, - 2130903671}; + 2130903349, + 2130903419, + 2130903438, + 2130903644, + 2130903672}; // aapt resource value: 0 public const int AppCompatTextView_android_textAppearance = 0; @@ -21416,7 +21425,7 @@ namespace TINK.Droid // aapt resource value: 20 public const int AppCompatTextView_textLocale = 20; - // aapt resource value: { 0x1010057,0x10100AE,0x7F030000,0x7F030001,0x7F030002,0x7F030003,0x7F030004,0x7F030005,0x7F030006,0x7F030007,0x7F030008,0x7F030009,0x7F03000A,0x7F03000B,0x7F03000C,0x7F03000E,0x7F03000F,0x7F030010,0x7F030011,0x7F030012,0x7F030013,0x7F030014,0x7F030015,0x7F030016,0x7F030017,0x7F030018,0x7F030019,0x7F03001A,0x7F03001B,0x7F03001C,0x7F03001D,0x7F03001E,0x7F030022,0x7F030023,0x7F030024,0x7F030025,0x7F030026,0x7F03002F,0x7F030050,0x7F030060,0x7F030061,0x7F030062,0x7F030063,0x7F030064,0x7F03006A,0x7F03006B,0x7F03007D,0x7F030084,0x7F0300A8,0x7F0300A9,0x7F0300AA,0x7F0300AB,0x7F0300AC,0x7F0300AD,0x7F0300AE,0x7F0300B5,0x7F0300B6,0x7F0300BD,0x7F0300CC,0x7F0300E5,0x7F0300E6,0x7F0300E7,0x7F0300EA,0x7F0300EC,0x7F0300F8,0x7F0300F9,0x7F0300FA,0x7F0300FB,0x7F0300FC,0x7F030149,0x7F030158,0x7F03018F,0x7F030190,0x7F030191,0x7F030192,0x7F030195,0x7F030196,0x7F030197,0x7F030198,0x7F030199,0x7F03019A,0x7F03019B,0x7F03019C,0x7F03019D,0x7F0301DD,0x7F0301DE,0x7F0301DF,0x7F0301E9,0x7F0301EB,0x7F0301F5,0x7F0301F7,0x7F0301F8,0x7F0301F9,0x7F030204,0x7F030205,0x7F030206,0x7F030207,0x7F03021D,0x7F03021E,0x7F03023F,0x7F030266,0x7F030268,0x7F030269,0x7F03026A,0x7F03026C,0x7F03026D,0x7F03026E,0x7F03026F,0x7F030272,0x7F030273,0x7F030296,0x7F030297,0x7F030298,0x7F030299,0x7F0302B2,0x7F0302B4,0x7F0302B5,0x7F0302B6,0x7F0302B7,0x7F0302B8,0x7F0302B9,0x7F0302BA,0x7F0302BB,0x7F0302BC,0x7F0302BD } + // aapt resource value: { 0x1010057,0x10100AE,0x7F030000,0x7F030001,0x7F030002,0x7F030003,0x7F030004,0x7F030005,0x7F030006,0x7F030007,0x7F030008,0x7F030009,0x7F03000A,0x7F03000B,0x7F03000C,0x7F03000E,0x7F03000F,0x7F030010,0x7F030011,0x7F030012,0x7F030013,0x7F030014,0x7F030015,0x7F030016,0x7F030017,0x7F030018,0x7F030019,0x7F03001A,0x7F03001B,0x7F03001C,0x7F03001D,0x7F03001E,0x7F030022,0x7F030023,0x7F030024,0x7F030025,0x7F030026,0x7F03002F,0x7F030050,0x7F030060,0x7F030061,0x7F030062,0x7F030063,0x7F030064,0x7F03006A,0x7F03006B,0x7F03007D,0x7F030084,0x7F0300A8,0x7F0300A9,0x7F0300AA,0x7F0300AB,0x7F0300AC,0x7F0300AD,0x7F0300AE,0x7F0300B5,0x7F0300B6,0x7F0300BD,0x7F0300CC,0x7F0300E5,0x7F0300E6,0x7F0300E7,0x7F0300EA,0x7F0300EC,0x7F0300F8,0x7F0300F9,0x7F0300FA,0x7F0300FB,0x7F0300FC,0x7F03014A,0x7F030159,0x7F030190,0x7F030191,0x7F030192,0x7F030193,0x7F030196,0x7F030197,0x7F030198,0x7F030199,0x7F03019A,0x7F03019B,0x7F03019C,0x7F03019D,0x7F03019E,0x7F0301DE,0x7F0301DF,0x7F0301E0,0x7F0301EA,0x7F0301EC,0x7F0301F6,0x7F0301F8,0x7F0301F9,0x7F0301FA,0x7F030205,0x7F030206,0x7F030207,0x7F030208,0x7F03021E,0x7F03021F,0x7F030240,0x7F030267,0x7F030269,0x7F03026A,0x7F03026B,0x7F03026D,0x7F03026E,0x7F03026F,0x7F030270,0x7F030273,0x7F030274,0x7F030297,0x7F030298,0x7F030299,0x7F03029A,0x7F0302B3,0x7F0302B5,0x7F0302B6,0x7F0302B7,0x7F0302B8,0x7F0302B9,0x7F0302BA,0x7F0302BB,0x7F0302BC,0x7F0302BD,0x7F0302BE } public static int[] AppCompatTheme = new int[] { 16842839, 16842926, @@ -21487,13 +21496,12 @@ namespace TINK.Droid 2130903290, 2130903291, 2130903292, - 2130903369, - 2130903384, - 2130903439, + 2130903370, + 2130903385, 2130903440, 2130903441, 2130903442, - 2130903445, + 2130903443, 2130903446, 2130903447, 2130903448, @@ -21502,38 +21510,38 @@ namespace TINK.Droid 2130903451, 2130903452, 2130903453, - 2130903517, + 2130903454, 2130903518, 2130903519, - 2130903529, - 2130903531, - 2130903541, - 2130903543, + 2130903520, + 2130903530, + 2130903532, + 2130903542, 2130903544, 2130903545, - 2130903556, + 2130903546, 2130903557, 2130903558, 2130903559, - 2130903581, + 2130903560, 2130903582, - 2130903615, - 2130903654, - 2130903656, + 2130903583, + 2130903616, + 2130903655, 2130903657, 2130903658, - 2130903660, + 2130903659, 2130903661, 2130903662, 2130903663, - 2130903666, + 2130903664, 2130903667, - 2130903702, + 2130903668, 2130903703, 2130903704, 2130903705, - 2130903730, - 2130903732, + 2130903706, + 2130903731, 2130903733, 2130903734, 2130903735, @@ -21542,7 +21550,8 @@ namespace TINK.Droid 2130903738, 2130903739, 2130903740, - 2130903741}; + 2130903741, + 2130903742}; // aapt resource value: 2 public const int AppCompatTheme_actionBarDivider = 2; @@ -21919,15 +21928,15 @@ namespace TINK.Droid // aapt resource value: 124 public const int AppCompatTheme_windowNoTitle = 124; - // aapt resource value: { 0x7F030036,0x7F030040,0x7F030042,0x7F03014B,0x7F0301B8,0x7F0301D3,0x7F0302B1 } + // aapt resource value: { 0x7F030036,0x7F030040,0x7F030042,0x7F03014C,0x7F0301B9,0x7F0301D4,0x7F0302B2 } public static int[] Badge = new int[] { 2130903094, 2130903104, 2130903106, - 2130903371, - 2130903480, - 2130903507, - 2130903729}; + 2130903372, + 2130903481, + 2130903508, + 2130903730}; // aapt resource value: 0 public const int Badge_backgroundColor = 0; @@ -21950,7 +21959,7 @@ namespace TINK.Droid // aapt resource value: 6 public const int Badge_verticalOffset = 6; - // aapt resource value: { 0x7F03003E,0x7F0300FD,0x7F03011D,0x7F03011E,0x7F03011F,0x7F030120,0x7F030121,0x7F030144,0x7F0301D7,0x7F0301D9,0x7F0301DA } + // aapt resource value: { 0x7F03003E,0x7F0300FD,0x7F03011D,0x7F03011E,0x7F03011F,0x7F030120,0x7F030121,0x7F030145,0x7F0301D8,0x7F0301DA,0x7F0301DB } public static int[] BottomAppBar = new int[] { 2130903102, 2130903293, @@ -21959,10 +21968,10 @@ namespace TINK.Droid 2130903327, 2130903328, 2130903329, - 2130903364, - 2130903511, - 2130903513, - 2130903514}; + 2130903365, + 2130903512, + 2130903514, + 2130903515}; // aapt resource value: 0 public const int BottomAppBar_backgroundTint = 0; @@ -21997,20 +22006,20 @@ namespace TINK.Droid // aapt resource value: 10 public const int BottomAppBar_paddingRightSystemWindowInsets = 10; - // aapt resource value: { 0x7F03003E,0x7F0300FD,0x7F03015E,0x7F030161,0x7F030163,0x7F030164,0x7F030167,0x7F030173,0x7F030174,0x7F030175,0x7F030179,0x7F0301CC } + // aapt resource value: { 0x7F03003E,0x7F0300FD,0x7F03015F,0x7F030162,0x7F030164,0x7F030165,0x7F030168,0x7F030174,0x7F030175,0x7F030176,0x7F03017A,0x7F0301CD } public static int[] BottomNavigationView = new int[] { 2130903102, 2130903293, - 2130903390, - 2130903393, - 2130903395, + 2130903391, + 2130903394, 2130903396, - 2130903399, - 2130903411, + 2130903397, + 2130903400, 2130903412, 2130903413, - 2130903417, - 2130903500}; + 2130903414, + 2130903418, + 2130903501}; // aapt resource value: 0 public const int BottomNavigationView_backgroundTint = 0; @@ -22048,7 +22057,7 @@ namespace TINK.Droid // aapt resource value: 11 public const int BottomNavigationView_menu = 11; - // aapt resource value: { 0x1010440,0x7F03003E,0x7F030046,0x7F030047,0x7F030048,0x7F030049,0x7F03004A,0x7F03004C,0x7F03004D,0x7F03004E,0x7F030138,0x7F030209,0x7F03020C } + // aapt resource value: { 0x1010440,0x7F03003E,0x7F030046,0x7F030047,0x7F030048,0x7F030049,0x7F03004A,0x7F03004C,0x7F03004D,0x7F03004E,0x7F030139,0x7F03020A,0x7F03020D } public static int[] BottomSheetBehavior_Layout = new int[] { 16843840, 2130903102, @@ -22060,9 +22069,9 @@ namespace TINK.Droid 2130903116, 2130903117, 2130903118, - 2130903352, - 2130903561, - 2130903564}; + 2130903353, + 2130903562, + 2130903565}; // aapt resource value: 0 public const int BottomSheetBehavior_Layout_android_elevation = 0; @@ -22165,7 +22174,7 @@ namespace TINK.Droid // aapt resource value: 12 public const int CardView_contentPaddingTop = 12; - // aapt resource value: { 0x1010034,0x1010098,0x10100AB,0x101011F,0x101014F,0x10101E5,0x7F030080,0x7F030081,0x7F030082,0x7F030083,0x7F030085,0x7F030086,0x7F030087,0x7F030089,0x7F03008A,0x7F03008B,0x7F03008C,0x7F03008D,0x7F03008E,0x7F03008F,0x7F030094,0x7F030095,0x7F030096,0x7F030098,0x7F03009A,0x7F03009B,0x7F03009C,0x7F03009D,0x7F03009E,0x7F03009F,0x7F0300A0,0x7F030108,0x7F030142,0x7F03014E,0x7F030152,0x7F0301FC,0x7F030209,0x7F03020C,0x7F030210,0x7F030274,0x7F030278 } + // aapt resource value: { 0x1010034,0x1010098,0x10100AB,0x101011F,0x101014F,0x10101E5,0x7F030080,0x7F030081,0x7F030082,0x7F030083,0x7F030085,0x7F030086,0x7F030087,0x7F030089,0x7F03008A,0x7F03008B,0x7F03008C,0x7F03008D,0x7F03008E,0x7F03008F,0x7F030094,0x7F030095,0x7F030096,0x7F030098,0x7F03009A,0x7F03009B,0x7F03009C,0x7F03009D,0x7F03009E,0x7F03009F,0x7F0300A0,0x7F030108,0x7F030143,0x7F03014F,0x7F030153,0x7F0301FD,0x7F03020A,0x7F03020D,0x7F030211,0x7F030275,0x7F030279 } public static int[] Chip = new int[] { 16842804, 16842904, @@ -22199,25 +22208,25 @@ namespace TINK.Droid 2130903199, 2130903200, 2130903304, - 2130903362, - 2130903374, - 2130903378, - 2130903548, - 2130903561, - 2130903564, - 2130903568, - 2130903668, - 2130903672}; + 2130903363, + 2130903375, + 2130903379, + 2130903549, + 2130903562, + 2130903565, + 2130903569, + 2130903669, + 2130903673}; - // aapt resource value: { 0x7F03007F,0x7F030090,0x7F030091,0x7F030092,0x7F030208,0x7F030215,0x7F030216 } + // aapt resource value: { 0x7F03007F,0x7F030090,0x7F030091,0x7F030092,0x7F030209,0x7F030216,0x7F030217 } public static int[] ChipGroup = new int[] { 2130903167, 2130903184, 2130903185, 2130903186, - 2130903560, - 2130903573, - 2130903574}; + 2130903561, + 2130903574, + 2130903575}; // aapt resource value: 0 public const int ChipGroup_checkedChip = 0; @@ -22363,7 +22372,7 @@ namespace TINK.Droid // aapt resource value: 40 public const int Chip_textStartPadding = 40; - // aapt resource value: { 0x7F0300A4,0x7F0300A5,0x7F0300CB,0x7F030112,0x7F030113,0x7F030114,0x7F030115,0x7F030116,0x7F030117,0x7F030118,0x7F0301BA,0x7F0301FE,0x7F030200,0x7F03022F,0x7F03028A,0x7F03028B,0x7F030295 } + // aapt resource value: { 0x7F0300A4,0x7F0300A5,0x7F0300CB,0x7F030112,0x7F030113,0x7F030114,0x7F030115,0x7F030116,0x7F030117,0x7F030118,0x7F0301BB,0x7F0301FF,0x7F030201,0x7F030230,0x7F03028B,0x7F03028C,0x7F030296 } public static int[] CollapsingToolbarLayout = new int[] { 2130903204, 2130903205, @@ -22375,13 +22384,13 @@ namespace TINK.Droid 2130903318, 2130903319, 2130903320, - 2130903482, - 2130903550, - 2130903552, - 2130903599, - 2130903690, + 2130903483, + 2130903551, + 2130903553, + 2130903600, 2130903691, - 2130903701}; + 2130903692, + 2130903702}; // aapt resource value: 0 public const int CollapsingToolbarLayout_collapsedTitleGravity = 0; @@ -22413,10 +22422,10 @@ namespace TINK.Droid // aapt resource value: 9 public const int CollapsingToolbarLayout_expandedTitleTextAppearance = 9; - // aapt resource value: { 0x7F030184,0x7F030185 } + // aapt resource value: { 0x7F030185,0x7F030186 } public static int[] CollapsingToolbarLayout_Layout = new int[] { - 2130903428, - 2130903429}; + 2130903429, + 2130903430}; // aapt resource value: 0 public const int CollapsingToolbarLayout_Layout_layout_collapseMode = 0; @@ -22479,23 +22488,23 @@ namespace TINK.Droid // aapt resource value: 3 public const int CompoundButton_buttonTintMode = 3; - // aapt resource value: { 0x7F030176,0x7F03022D } + // aapt resource value: { 0x7F030177,0x7F03022E } public static int[] CoordinatorLayout = new int[] { - 2130903414, - 2130903597}; + 2130903415, + 2130903598}; // aapt resource value: 0 public const int CoordinatorLayout_keylines = 0; - // aapt resource value: { 0x10100B3,0x7F030181,0x7F030182,0x7F030183,0x7F030186,0x7F030187,0x7F030188 } + // aapt resource value: { 0x10100B3,0x7F030182,0x7F030183,0x7F030184,0x7F030187,0x7F030188,0x7F030189 } public static int[] CoordinatorLayout_Layout = new int[] { 16842931, - 2130903425, 2130903426, 2130903427, - 2130903430, + 2130903428, 2130903431, - 2130903432}; + 2130903432, + 2130903433}; // aapt resource value: 0 public const int CoordinatorLayout_Layout_android_layout_gravity = 0; @@ -22521,16 +22530,16 @@ namespace TINK.Droid // aapt resource value: 1 public const int CoordinatorLayout_statusBarBackground = 1; - // aapt resource value: { 0x7F03002D,0x7F03002E,0x7F030043,0x7F0300A7,0x7F0300F1,0x7F030137,0x7F03021C,0x7F03027B } + // aapt resource value: { 0x7F03002D,0x7F03002E,0x7F030043,0x7F0300A7,0x7F0300F1,0x7F030138,0x7F03021D,0x7F03027C } public static int[] DrawerArrowToggle = new int[] { 2130903085, 2130903086, 2130903107, 2130903207, 2130903281, - 2130903351, - 2130903580, - 2130903675}; + 2130903352, + 2130903581, + 2130903676}; // aapt resource value: 0 public const int DrawerArrowToggle_arrowHeadLength = 0; @@ -22563,13 +22572,13 @@ namespace TINK.Droid // aapt resource value: 0 public const int DrawerLayout_elevation = 0; - // aapt resource value: { 0x7F0300FD,0x7F030119,0x7F030142,0x7F030210,0x7F030213 } + // aapt resource value: { 0x7F0300FD,0x7F030119,0x7F030143,0x7F030211,0x7F030214 } public static int[] ExtendedFloatingActionButton = new int[] { 2130903293, 2130903321, - 2130903362, - 2130903568, - 2130903571}; + 2130903363, + 2130903569, + 2130903572}; // aapt resource value: { 0x7F030044,0x7F030045 } public static int[] ExtendedFloatingActionButton_Behavior_Layout = new int[] { @@ -22597,7 +22606,7 @@ namespace TINK.Droid // aapt resource value: 4 public const int ExtendedFloatingActionButton_shrinkMotionSpec = 4; - // aapt resource value: { 0x101000E,0x7F03003E,0x7F03003F,0x7F03004F,0x7F0300FD,0x7F030108,0x7F030122,0x7F030123,0x7F030142,0x7F03014C,0x7F0301B9,0x7F0301F0,0x7F0301FC,0x7F030209,0x7F03020C,0x7F030210,0x7F0302AD } + // aapt resource value: { 0x101000E,0x7F03003E,0x7F03003F,0x7F03004F,0x7F0300FD,0x7F030108,0x7F030122,0x7F030123,0x7F030143,0x7F03014D,0x7F0301BA,0x7F0301F1,0x7F0301FD,0x7F03020A,0x7F03020D,0x7F030211,0x7F0302AE } public static int[] FloatingActionButton = new int[] { 16842766, 2130903102, @@ -22607,15 +22616,15 @@ namespace TINK.Droid 2130903304, 2130903330, 2130903331, - 2130903362, - 2130903372, - 2130903481, - 2130903536, - 2130903548, - 2130903561, - 2130903564, - 2130903568, - 2130903725}; + 2130903363, + 2130903373, + 2130903482, + 2130903537, + 2130903549, + 2130903562, + 2130903565, + 2130903569, + 2130903726}; // aapt resource value: 0 public const int FloatingActionButton_android_enabled = 0; @@ -22675,10 +22684,10 @@ namespace TINK.Droid // aapt resource value: 16 public const int FloatingActionButton_useCompatPadding = 16; - // aapt resource value: { 0x7F03016F,0x7F03018E } + // aapt resource value: { 0x7F030170,0x7F03018F } public static int[] FlowLayout = new int[] { - 2130903407, - 2130903438}; + 2130903408, + 2130903439}; // aapt resource value: 0 public const int FlowLayout_itemSpacing = 0; @@ -22686,16 +22695,17 @@ namespace TINK.Droid // aapt resource value: 1 public const int FlowLayout_lineSpacing = 1; - // aapt resource value: { 0x7F03012D,0x7F03012E,0x7F03012F,0x7F030130,0x7F030131,0x7F030132 } + // aapt resource value: { 0x7F03012D,0x7F03012E,0x7F03012F,0x7F030130,0x7F030131,0x7F030132,0x7F030133 } public static int[] FontFamily = new int[] { 2130903341, 2130903342, 2130903343, 2130903344, 2130903345, - 2130903346}; + 2130903346, + 2130903347}; - // aapt resource value: { 0x1010532,0x1010533,0x101053F,0x101056F,0x1010570,0x7F03012B,0x7F030133,0x7F030134,0x7F030135,0x7F0302A4 } + // aapt resource value: { 0x1010532,0x1010533,0x101053F,0x101056F,0x1010570,0x7F03012B,0x7F030134,0x7F030135,0x7F030136,0x7F0302A5 } public static int[] FontFamilyFont = new int[] { 16844082, 16844083, @@ -22703,10 +22713,10 @@ namespace TINK.Droid 16844143, 16844144, 2130903339, - 2130903347, 2130903348, 2130903349, - 2130903716}; + 2130903350, + 2130903717}; // aapt resource value: 0 public const int FontFamilyFont_android_font = 0; @@ -22756,11 +22766,14 @@ namespace TINK.Droid // aapt resource value: 5 public const int FontFamily_fontProviderQuery = 5; - // aapt resource value: { 0x1010109,0x1010200,0x7F030136 } + // aapt resource value: 6 + public const int FontFamily_fontProviderSystemFontFamily = 6; + + // aapt resource value: { 0x1010109,0x1010200,0x7F030137 } public static int[] ForegroundLinearLayout = new int[] { 16843017, 16843264, - 2130903350}; + 2130903351}; // aapt resource value: 0 public const int ForegroundLinearLayout_android_foreground = 0; @@ -22859,11 +22872,11 @@ namespace TINK.Droid // aapt resource value: 2 public const int GradientColor_android_type = 2; - // aapt resource value: { 0x7F0301D7,0x7F0301D9,0x7F0301DA } + // aapt resource value: { 0x7F0301D8,0x7F0301DA,0x7F0301DB } public static int[] Insets = new int[] { - 2130903511, - 2130903513, - 2130903514}; + 2130903512, + 2130903514, + 2130903515}; // aapt resource value: 0 public const int Insets_paddingBottomSystemWindowInsets = 0; @@ -22881,7 +22894,7 @@ namespace TINK.Droid // aapt resource value: 0 public const int ItemsViewRendererTheme_collectionViewStyle = 0; - // aapt resource value: { 0x10100AF,0x10100C4,0x1010126,0x1010127,0x1010128,0x7F0300E9,0x7F0300EB,0x7F0301BB,0x7F03020F } + // aapt resource value: { 0x10100AF,0x10100C4,0x1010126,0x1010127,0x1010128,0x7F0300E9,0x7F0300EB,0x7F0301BC,0x7F030210 } public static int[] LinearLayoutCompat = new int[] { 16842927, 16842948, @@ -22890,8 +22903,8 @@ namespace TINK.Droid 16843048, 2130903273, 2130903275, - 2130903483, - 2130903567}; + 2130903484, + 2130903568}; // aapt resource value: 2 public const int LinearLayoutCompat_android_baselineAligned = 2; @@ -22950,11 +22963,11 @@ namespace TINK.Droid // aapt resource value: 1 public const int ListPopupWindow_android_dropDownVerticalOffset = 1; - // aapt resource value: { 0x7F030099,0x7F030156,0x7F030157 } + // aapt resource value: { 0x7F030099,0x7F030157,0x7F030158 } public static int[] LoadingImageView = new int[] { 2130903193, - 2130903382, - 2130903383}; + 2130903383, + 2130903384}; // aapt resource value: 0 public const int LoadingImageView_circleCrop = 0; @@ -22965,7 +22978,7 @@ namespace TINK.Droid // aapt resource value: 2 public const int LoadingImageView_imageAspectRatioAdjust = 2; - // aapt resource value: { 0x7F03002A,0x7F03006E,0x7F03006F,0x7F030070,0x7F030071,0x7F030072,0x7F030073,0x7F030074,0x7F03017B,0x7F03017C,0x7F03017D,0x7F03017E,0x7F03019E,0x7F0301A1,0x7F0302A5,0x7F0302A6,0x7F0302A7,0x7F0302A8,0x7F0302A9,0x7F0302AA,0x7F0302AB,0x7F0302AC,0x7F0302AF,0x7F0302C1 } + // aapt resource value: { 0x7F03002A,0x7F03006E,0x7F03006F,0x7F030070,0x7F030071,0x7F030072,0x7F030073,0x7F030074,0x7F03017C,0x7F03017D,0x7F03017E,0x7F03017F,0x7F03019F,0x7F0301A2,0x7F0302A6,0x7F0302A7,0x7F0302A8,0x7F0302A9,0x7F0302AA,0x7F0302AB,0x7F0302AC,0x7F0302AD,0x7F0302B0,0x7F0302C2 } public static int[] MapAttrs = new int[] { 2130903082, 2130903150, @@ -22975,13 +22988,12 @@ namespace TINK.Droid 2130903154, 2130903155, 2130903156, - 2130903419, 2130903420, 2130903421, 2130903422, - 2130903454, - 2130903457, - 2130903717, + 2130903423, + 2130903455, + 2130903458, 2130903718, 2130903719, 2130903720, @@ -22989,8 +23001,9 @@ namespace TINK.Droid 2130903722, 2130903723, 2130903724, - 2130903727, - 2130903745}; + 2130903725, + 2130903728, + 2130903746}; // aapt resource value: 0 public const int MapAttrs_ambientEnabled = 0; @@ -23071,13 +23084,13 @@ namespace TINK.Droid 2130903097, 2130903098}; - // aapt resource value: { 0x7F0301A2,0x7F0301A3,0x7F0301A4,0x7F0301A5,0x7F0301A6 } + // aapt resource value: { 0x7F0301A3,0x7F0301A4,0x7F0301A5,0x7F0301A6,0x7F0301A7 } public static int[] MaterialAlertDialogTheme = new int[] { - 2130903458, 2130903459, 2130903460, 2130903461, - 2130903462}; + 2130903462, + 2130903463}; // aapt resource value: 0 public const int MaterialAlertDialogTheme_materialAlertDialogBodyTextStyle = 0; @@ -23113,7 +23126,7 @@ namespace TINK.Droid // aapt resource value: 0 public const int MaterialAutoCompleteTextView_android_inputType = 0; - // aapt resource value: { 0x10100D4,0x10101B7,0x10101B8,0x10101B9,0x10101BA,0x10101E5,0x7F03003E,0x7F03003F,0x7F0300D3,0x7F0300FD,0x7F03014D,0x7F03014F,0x7F030150,0x7F030151,0x7F030153,0x7F030154,0x7F0301FC,0x7F030209,0x7F03020C,0x7F030230,0x7F030231 } + // aapt resource value: { 0x10100D4,0x10101B7,0x10101B8,0x10101B9,0x10101BA,0x10101E5,0x7F03003E,0x7F03003F,0x7F0300D3,0x7F0300FD,0x7F03014E,0x7F030150,0x7F030151,0x7F030152,0x7F030154,0x7F030155,0x7F0301FD,0x7F03020A,0x7F03020D,0x7F030231,0x7F030232 } public static int[] MaterialButton = new int[] { 16842964, 16843191, @@ -23125,23 +23138,23 @@ namespace TINK.Droid 2130903103, 2130903251, 2130903293, - 2130903373, - 2130903375, + 2130903374, 2130903376, 2130903377, - 2130903379, + 2130903378, 2130903380, - 2130903548, - 2130903561, - 2130903564, - 2130903600, - 2130903601}; + 2130903381, + 2130903549, + 2130903562, + 2130903565, + 2130903601, + 2130903602}; - // aapt resource value: { 0x7F03007E,0x7F030208,0x7F030216 } + // aapt resource value: { 0x7F03007E,0x7F030209,0x7F030217 } public static int[] MaterialButtonToggleGroup = new int[] { 2130903166, - 2130903560, - 2130903574}; + 2130903561, + 2130903575}; // aapt resource value: 0 public const int MaterialButtonToggleGroup_checkedButton = 0; @@ -23215,30 +23228,30 @@ namespace TINK.Droid // aapt resource value: 20 public const int MaterialButton_strokeWidth = 20; - // aapt resource value: { 0x101020D,0x7F0300E0,0x7F0300E1,0x7F0300E2,0x7F0300E3,0x7F0301F6,0x7F0302BE,0x7F0302BF,0x7F0302C0 } + // aapt resource value: { 0x101020D,0x7F0300E0,0x7F0300E1,0x7F0300E2,0x7F0300E3,0x7F0301F7,0x7F0302BF,0x7F0302C0,0x7F0302C1 } public static int[] MaterialCalendar = new int[] { 16843277, 2130903264, 2130903265, 2130903266, 2130903267, - 2130903542, - 2130903742, + 2130903543, 2130903743, - 2130903744}; + 2130903744, + 2130903745}; - // aapt resource value: { 0x10101B7,0x10101B8,0x10101B9,0x10101BA,0x7F03015F,0x7F030168,0x7F030169,0x7F030170,0x7F030171,0x7F030175 } + // aapt resource value: { 0x10101B7,0x10101B8,0x10101B9,0x10101BA,0x7F030160,0x7F030169,0x7F03016A,0x7F030171,0x7F030172,0x7F030176 } public static int[] MaterialCalendarItem = new int[] { 16843191, 16843192, 16843193, 16843194, - 2130903391, - 2130903400, + 2130903392, 2130903401, - 2130903408, + 2130903402, 2130903409, - 2130903413}; + 2130903410, + 2130903414}; // aapt resource value: 3 public const int MaterialCalendarItem_android_insetBottom = 3; @@ -23297,18 +23310,18 @@ namespace TINK.Droid // aapt resource value: 8 public const int MaterialCalendar_yearTodayStyle = 8; - // aapt resource value: { 0x10101E5,0x7F030078,0x7F030080,0x7F030082,0x7F0301FC,0x7F030209,0x7F03020C,0x7F03022A,0x7F030230,0x7F030231 } + // aapt resource value: { 0x10101E5,0x7F030078,0x7F030080,0x7F030082,0x7F0301FD,0x7F03020A,0x7F03020D,0x7F03022B,0x7F030231,0x7F030232 } public static int[] MaterialCardView = new int[] { 16843237, 2130903160, 2130903168, 2130903170, - 2130903548, - 2130903561, - 2130903564, - 2130903594, - 2130903600, - 2130903601}; + 2130903549, + 2130903562, + 2130903565, + 2130903595, + 2130903601, + 2130903602}; // aapt resource value: 0 public const int MaterialCardView_android_checkable = 0; @@ -23340,10 +23353,10 @@ namespace TINK.Droid // aapt resource value: 9 public const int MaterialCardView_strokeWidth = 9; - // aapt resource value: { 0x7F03006C,0x7F0302AE } + // aapt resource value: { 0x7F03006C,0x7F0302AF } public static int[] MaterialCheckBox = new int[] { 2130903148, - 2130903726}; + 2130903727}; // aapt resource value: 0 public const int MaterialCheckBox_buttonTint = 0; @@ -23351,10 +23364,10 @@ namespace TINK.Droid // aapt resource value: 1 public const int MaterialCheckBox_useMaterialThemeColors = 1; - // aapt resource value: { 0x7F03006C,0x7F0302AE } + // aapt resource value: { 0x7F03006C,0x7F0302AF } public static int[] MaterialRadioButton = new int[] { 2130903148, - 2130903726}; + 2130903727}; // aapt resource value: 0 public const int MaterialRadioButton_buttonTint = 0; @@ -23362,10 +23375,10 @@ namespace TINK.Droid // aapt resource value: 1 public const int MaterialRadioButton_useMaterialThemeColors = 1; - // aapt resource value: { 0x7F030209,0x7F03020C } + // aapt resource value: { 0x7F03020A,0x7F03020D } public static int[] MaterialShape = new int[] { - 2130903561, - 2130903564}; + 2130903562, + 2130903565}; // aapt resource value: 0 public const int MaterialShape_shapeAppearance = 0; @@ -23373,10 +23386,10 @@ namespace TINK.Droid // aapt resource value: 1 public const int MaterialShape_shapeAppearanceOverlay = 1; - // aapt resource value: { 0x101057F,0x7F03018D } + // aapt resource value: { 0x101057F,0x7F03018E } public static int[] MaterialTextAppearance = new int[] { 16844159, - 2130903437}; + 2130903438}; // aapt resource value: 0 public const int MaterialTextAppearance_android_lineHeight = 0; @@ -23384,11 +23397,11 @@ namespace TINK.Droid // aapt resource value: 1 public const int MaterialTextAppearance_lineHeight = 1; - // aapt resource value: { 0x1010034,0x101057F,0x7F03018D } + // aapt resource value: { 0x1010034,0x101057F,0x7F03018E } public static int[] MaterialTextView = new int[] { 16842804, 16844159, - 2130903437}; + 2130903438}; // aapt resource value: 1 public const int MaterialTextView_android_lineHeight = 1; @@ -23399,13 +23412,13 @@ namespace TINK.Droid // aapt resource value: 2 public const int MaterialTextView_lineHeight = 2; - // aapt resource value: { 0x101013F,0x1010140,0x7F03011B,0x7F03011C,0x7F0301BF } + // aapt resource value: { 0x101013F,0x1010140,0x7F03011B,0x7F03011C,0x7F0301C0 } public static int[] MediaRouteButton = new int[] { 16843071, 16843072, 2130903323, 2130903324, - 2130903487}; + 2130903488}; // aapt resource value: 1 public const int MediaRouteButton_android_minHeight = 1; @@ -23449,7 +23462,7 @@ namespace TINK.Droid // aapt resource value: 2 public const int MenuGroup_android_visible = 2; - // aapt resource value: { 0x1010002,0x101000E,0x10100D0,0x1010106,0x1010194,0x10101DE,0x10101DF,0x10101E1,0x10101E2,0x10101E3,0x10101E4,0x10101E5,0x101026F,0x7F03000D,0x7F03001F,0x7F030021,0x7F030029,0x7F0300BF,0x7F030153,0x7F030154,0x7F0301D4,0x7F03020E,0x7F03029B } + // aapt resource value: { 0x1010002,0x101000E,0x10100D0,0x1010106,0x1010194,0x10101DE,0x10101DF,0x10101E1,0x10101E2,0x10101E3,0x10101E4,0x10101E5,0x101026F,0x7F03000D,0x7F03001F,0x7F030021,0x7F030029,0x7F0300BF,0x7F030154,0x7F030155,0x7F0301D5,0x7F03020F,0x7F03029C } public static int[] MenuItem = new int[] { 16842754, 16842766, @@ -23469,11 +23482,11 @@ namespace TINK.Droid 2130903073, 2130903081, 2130903231, - 2130903379, 2130903380, - 2130903508, - 2130903566, - 2130903707}; + 2130903381, + 2130903509, + 2130903567, + 2130903708}; // aapt resource value: 13 public const int MenuItem_actionLayout = 13; @@ -23544,7 +23557,7 @@ namespace TINK.Droid // aapt resource value: 22 public const int MenuItem_tooltipText = 22; - // aapt resource value: { 0x10100AE,0x101012C,0x101012D,0x101012E,0x101012F,0x1010130,0x1010131,0x7F0301EF,0x7F030232 } + // aapt resource value: { 0x10100AE,0x101012C,0x101012D,0x101012E,0x101012F,0x1010130,0x1010131,0x7F0301F0,0x7F030233 } public static int[] MenuView = new int[] { 16842926, 16843052, @@ -23553,8 +23566,8 @@ namespace TINK.Droid 16843055, 16843056, 16843057, - 2130903535, - 2130903602}; + 2130903536, + 2130903603}; // aapt resource value: 4 public const int MenuView_android_headerBackground = 4; @@ -23583,29 +23596,29 @@ namespace TINK.Droid // aapt resource value: 8 public const int MenuView_subMenuArrow = 8; - // aapt resource value: { 0x10100D4,0x10100DD,0x101011F,0x7F0300FD,0x7F03013C,0x7F03015E,0x7F030160,0x7F030162,0x7F030163,0x7F030164,0x7F030165,0x7F030168,0x7F030169,0x7F03016A,0x7F03016B,0x7F03016C,0x7F03016D,0x7F03016E,0x7F030172,0x7F030175,0x7F0301CC } + // aapt resource value: { 0x10100D4,0x10100DD,0x101011F,0x7F0300FD,0x7F03013D,0x7F03015F,0x7F030161,0x7F030163,0x7F030164,0x7F030165,0x7F030166,0x7F030169,0x7F03016A,0x7F03016B,0x7F03016C,0x7F03016D,0x7F03016E,0x7F03016F,0x7F030173,0x7F030176,0x7F0301CD } public static int[] NavigationView = new int[] { 16842964, 16842973, 16843039, 2130903293, - 2130903356, - 2130903390, - 2130903392, - 2130903394, + 2130903357, + 2130903391, + 2130903393, 2130903395, 2130903396, 2130903397, - 2130903400, + 2130903398, 2130903401, 2130903402, 2130903403, 2130903404, 2130903405, 2130903406, - 2130903410, - 2130903413, - 2130903500}; + 2130903407, + 2130903411, + 2130903414, + 2130903501}; // aapt resource value: 0 public const int NavigationView_android_background = 0; @@ -23670,15 +23683,15 @@ namespace TINK.Droid // aapt resource value: 20 public const int NavigationView_menu = 20; - // aapt resource value: { 0x1010176,0x10102C9,0x7F0301D5 } + // aapt resource value: { 0x1010176,0x10102C9,0x7F0301D6 } public static int[] PopupWindow = new int[] { 16843126, 16843465, - 2130903509}; + 2130903510}; - // aapt resource value: { 0x7F030227 } + // aapt resource value: { 0x7F030228 } public static int[] PopupWindowBackgroundState = new int[] { - 2130903591}; + 2130903592}; // aapt resource value: 0 public const int PopupWindowBackgroundState_state_above_anchor = 0; @@ -23692,17 +23705,17 @@ namespace TINK.Droid // aapt resource value: 2 public const int PopupWindow_overlapAnchor = 2; - // aapt resource value: { 0x7F0302B0 } + // aapt resource value: { 0x7F0302B1 } public static int[] RangeSlider = new int[] { - 2130903728}; + 2130903729}; // aapt resource value: 0 public const int RangeSlider_values = 0; - // aapt resource value: { 0x7F0301D6,0x7F0301DC } + // aapt resource value: { 0x7F0301D7,0x7F0301DD } public static int[] RecycleListView = new int[] { - 2130903510, - 2130903516}; + 2130903511, + 2130903517}; // aapt resource value: 0 public const int RecycleListView_paddingBottomNoButtons = 0; @@ -23710,7 +23723,7 @@ namespace TINK.Droid // aapt resource value: 1 public const int RecycleListView_paddingTopNoTitle = 1; - // aapt resource value: { 0x10100C4,0x10100EB,0x10100F1,0x7F030124,0x7F030125,0x7F030126,0x7F030127,0x7F030128,0x7F030180,0x7F0301FB,0x7F03021B,0x7F030221 } + // aapt resource value: { 0x10100C4,0x10100EB,0x10100F1,0x7F030124,0x7F030125,0x7F030126,0x7F030127,0x7F030128,0x7F030181,0x7F0301FC,0x7F03021C,0x7F030222 } public static int[] RecyclerView = new int[] { 16842948, 16842987, @@ -23720,10 +23733,10 @@ namespace TINK.Droid 2130903334, 2130903335, 2130903336, - 2130903424, - 2130903547, - 2130903579, - 2130903585}; + 2130903425, + 2130903548, + 2130903580, + 2130903586}; // aapt resource value: 1 public const int RecyclerView_android_clipToPadding = 1; @@ -23761,9 +23774,9 @@ namespace TINK.Droid // aapt resource value: 11 public const int RecyclerView_stackFromEnd = 11; - // aapt resource value: { 0x7F03015B } + // aapt resource value: { 0x7F03015C } public static int[] ScrimInsetsFrameLayout = new int[] { - 2130903387}; + 2130903388}; // aapt resource value: 0 public const int ScrimInsetsFrameLayout_insetForeground = 0; @@ -23775,14 +23788,14 @@ namespace TINK.Droid // aapt resource value: 0 public const int ScrollingViewBehavior_Layout_behavior_overlapTop = 0; - // aapt resource value: { 0x7F030201 } + // aapt resource value: { 0x7F030202 } public static int[] ScrollViewRendererTheme = new int[] { - 2130903553}; + 2130903554}; // aapt resource value: 0 public const int ScrollViewRendererTheme_scrollViewStyle = 0; - // aapt resource value: { 0x10100DA,0x101011F,0x1010220,0x1010264,0x7F03009A,0x7F0300BE,0x7F0300E4,0x7F030139,0x7F030155,0x7F03017F,0x7F0301F3,0x7F0301F4,0x7F030202,0x7F030203,0x7F030233,0x7F03023B,0x7F0302B3 } + // aapt resource value: { 0x10100DA,0x101011F,0x1010220,0x1010264,0x7F03009A,0x7F0300BE,0x7F0300E4,0x7F03013A,0x7F030156,0x7F030180,0x7F0301F4,0x7F0301F5,0x7F030203,0x7F030204,0x7F030234,0x7F03023C,0x7F0302B4 } public static int[] SearchView = new int[] { 16842970, 16843039, @@ -23791,16 +23804,16 @@ namespace TINK.Droid 2130903194, 2130903230, 2130903268, - 2130903353, - 2130903381, - 2130903423, - 2130903539, + 2130903354, + 2130903382, + 2130903424, 2130903540, - 2130903554, + 2130903541, 2130903555, - 2130903603, - 2130903611, - 2130903731}; + 2130903556, + 2130903604, + 2130903612, + 2130903732}; // aapt resource value: 0 public const int SearchView_android_focusable = 0; @@ -23853,12 +23866,12 @@ namespace TINK.Droid // aapt resource value: 16 public const int SearchView_voiceIcon = 16; - // aapt resource value: { 0x7F030209,0x7F03020C,0x7F030230,0x7F030231 } + // aapt resource value: { 0x7F03020A,0x7F03020D,0x7F030231,0x7F030232 } public static int[] ShapeableImageView = new int[] { - 2130903561, - 2130903564, - 2130903600, - 2130903601}; + 2130903562, + 2130903565, + 2130903601, + 2130903602}; // aapt resource value: 0 public const int ShapeableImageView_shapeAppearance = 0; @@ -23915,11 +23928,11 @@ namespace TINK.Droid // aapt resource value: 9 public const int ShapeAppearance_cornerSizeTopRight = 9; - // aapt resource value: { 0x7F030069,0x7F0300B9,0x7F0301FD } + // aapt resource value: { 0x7F030069,0x7F0300B9,0x7F0301FE } public static int[] SignInButton = new int[] { 2130903145, 2130903225, - 2130903549}; + 2130903550}; // aapt resource value: 0 public const int SignInButton_buttonSize = 0; @@ -23930,27 +23943,27 @@ namespace TINK.Droid // aapt resource value: 2 public const int SignInButton_scopeUris = 2; - // aapt resource value: { 0x101000E,0x1010024,0x1010146,0x10102DE,0x10102DF,0x7F03013A,0x7F03013B,0x7F030177,0x7F030178,0x7F03027C,0x7F03027D,0x7F03027E,0x7F030282,0x7F030283,0x7F030284,0x7F03029D,0x7F03029E,0x7F03029F,0x7F0302A0 } + // aapt resource value: { 0x101000E,0x1010024,0x1010146,0x10102DE,0x10102DF,0x7F03013B,0x7F03013C,0x7F030178,0x7F030179,0x7F03027D,0x7F03027E,0x7F03027F,0x7F030283,0x7F030284,0x7F030285,0x7F03029E,0x7F03029F,0x7F0302A0,0x7F0302A1 } public static int[] Slider = new int[] { 16842766, 16842788, 16843078, 16843486, 16843487, - 2130903354, 2130903355, - 2130903415, + 2130903356, 2130903416, - 2130903676, + 2130903417, 2130903677, 2130903678, - 2130903682, + 2130903679, 2130903683, 2130903684, - 2130903709, + 2130903685, 2130903710, 2130903711, - 2130903712}; + 2130903712, + 2130903713}; // aapt resource value: 0 public const int Slider_android_enabled = 0; @@ -24009,13 +24022,13 @@ namespace TINK.Droid // aapt resource value: 18 public const int Slider_trackHeight = 18; - // aapt resource value: { 0x7F030218,0x7F030219,0x7F03021A } + // aapt resource value: { 0x7F030219,0x7F03021A,0x7F03021B } public static int[] Snackbar = new int[] { - 2130903576, 2130903577, - 2130903578}; + 2130903578, + 2130903579}; - // aapt resource value: { 0x101011F,0x7F030020,0x7F03002B,0x7F03003B,0x7F03003E,0x7F03003F,0x7F0300FD,0x7F0301B6 } + // aapt resource value: { 0x101011F,0x7F030020,0x7F03002B,0x7F03003B,0x7F03003E,0x7F03003F,0x7F0300FD,0x7F0301B7 } public static int[] SnackbarLayout = new int[] { 16843039, 2130903072, @@ -24024,7 +24037,7 @@ namespace TINK.Droid 2130903102, 2130903103, 2130903293, - 2130903478}; + 2130903479}; // aapt resource value: 1 public const int SnackbarLayout_actionTextColorAlpha = 1; @@ -24059,13 +24072,13 @@ namespace TINK.Droid // aapt resource value: 2 public const int Snackbar_snackbarTextViewStyle = 2; - // aapt resource value: { 0x10100B2,0x1010176,0x101017B,0x1010262,0x7F0301EA } + // aapt resource value: { 0x10100B2,0x1010176,0x101017B,0x1010262,0x7F0301EB } public static int[] Spinner = new int[] { 16842930, 16843126, 16843131, 16843362, - 2130903530}; + 2130903531}; // aapt resource value: 3 public const int Spinner_android_dropDownWidth = 3; @@ -24116,29 +24129,29 @@ namespace TINK.Droid // aapt resource value: 1 public const int StateListDrawable_android_visible = 1; - // aapt resource value: { 0x7F03023C } + // aapt resource value: { 0x7F03023D } public static int[] SwipeRefreshLayout = new int[] { - 2130903612}; + 2130903613}; // aapt resource value: 0 public const int SwipeRefreshLayout_swipeRefreshLayoutProgressSpinnerBackgroundColor = 0; - // aapt resource value: { 0x1010124,0x1010125,0x1010142,0x7F030211,0x7F03021F,0x7F03023D,0x7F03023E,0x7F030240,0x7F03027F,0x7F030280,0x7F030281,0x7F03029C,0x7F0302A1,0x7F0302A2 } + // aapt resource value: { 0x1010124,0x1010125,0x1010142,0x7F030212,0x7F030220,0x7F03023E,0x7F03023F,0x7F030241,0x7F030280,0x7F030281,0x7F030282,0x7F03029D,0x7F0302A2,0x7F0302A3 } public static int[] SwitchCompat = new int[] { 16843044, 16843045, 16843074, - 2130903569, - 2130903583, - 2130903613, + 2130903570, + 2130903584, 2130903614, - 2130903616, - 2130903679, + 2130903615, + 2130903617, 2130903680, 2130903681, - 2130903708, - 2130903713, - 2130903714}; + 2130903682, + 2130903709, + 2130903714, + 2130903715}; // aapt resource value: 1 public const int SwitchCompat_android_textOff = 1; @@ -24182,9 +24195,9 @@ namespace TINK.Droid // aapt resource value: 13 public const int SwitchCompat_trackTintMode = 13; - // aapt resource value: { 0x7F0302AE } + // aapt resource value: { 0x7F0302AF } public static int[] SwitchMaterial = new int[] { - 2130903726}; + 2130903727}; // aapt resource value: 0 public const int SwitchMaterial_useMaterialThemeColors = 0; @@ -24204,9 +24217,8 @@ namespace TINK.Droid // aapt resource value: 2 public const int TabItem_android_text = 2; - // aapt resource value: { 0x7F030241,0x7F030242,0x7F030243,0x7F030244,0x7F030245,0x7F030246,0x7F030247,0x7F030248,0x7F030249,0x7F03024A,0x7F03024B,0x7F03024C,0x7F03024D,0x7F03024E,0x7F03024F,0x7F030250,0x7F030251,0x7F030252,0x7F030253,0x7F030254,0x7F030255,0x7F030256,0x7F030258,0x7F030259,0x7F03025A } + // aapt resource value: { 0x7F030242,0x7F030243,0x7F030244,0x7F030245,0x7F030246,0x7F030247,0x7F030248,0x7F030249,0x7F03024A,0x7F03024B,0x7F03024C,0x7F03024D,0x7F03024E,0x7F03024F,0x7F030250,0x7F030251,0x7F030252,0x7F030253,0x7F030254,0x7F030255,0x7F030256,0x7F030257,0x7F030259,0x7F03025A,0x7F03025B } public static int[] TabLayout = new int[] { - 2130903617, 2130903618, 2130903619, 2130903620, @@ -24228,9 +24240,10 @@ namespace TINK.Droid 2130903636, 2130903637, 2130903638, - 2130903640, + 2130903639, 2130903641, - 2130903642}; + 2130903642, + 2130903643}; // aapt resource value: 0 public const int TabLayout_tabBackground = 0; @@ -24307,7 +24320,7 @@ namespace TINK.Droid // aapt resource value: 24 public const int TabLayout_tabUnboundedRipple = 24; - // aapt resource value: { 0x1010095,0x1010096,0x1010097,0x1010098,0x101009A,0x101009B,0x1010161,0x1010162,0x1010163,0x1010164,0x10103AC,0x1010585,0x7F03012C,0x7F030134,0x7F03025B,0x7F030277 } + // aapt resource value: { 0x1010095,0x1010096,0x1010097,0x1010098,0x101009A,0x101009B,0x1010161,0x1010162,0x1010163,0x1010164,0x10103AC,0x1010585,0x7F03012C,0x7F030135,0x7F03025C,0x7F030278 } public static int[] TextAppearance = new int[] { 16842901, 16842902, @@ -24322,9 +24335,9 @@ namespace TINK.Droid 16843692, 16844165, 2130903340, - 2130903348, - 2130903643, - 2130903671}; + 2130903349, + 2130903644, + 2130903672}; // aapt resource value: 10 public const int TextAppearance_android_fontFamily = 10; @@ -24374,14 +24387,14 @@ namespace TINK.Droid // aapt resource value: 15 public const int TextAppearance_textLocale = 15; - // aapt resource value: { 0x7F030275 } + // aapt resource value: { 0x7F030276 } public static int[] TextInputEditText = new int[] { - 2130903669}; + 2130903670}; // aapt resource value: 0 public const int TextInputEditText_textInputLayoutFocusedRectEnabled = 0; - // aapt resource value: { 0x101000E,0x101009A,0x1010150,0x7F030055,0x7F030056,0x7F030057,0x7F030058,0x7F030059,0x7F03005A,0x7F03005B,0x7F03005C,0x7F03005D,0x7F03005E,0x7F03005F,0x7F0300D9,0x7F0300DA,0x7F0300DB,0x7F0300DC,0x7F0300DD,0x7F0300DE,0x7F030100,0x7F030101,0x7F030102,0x7F030103,0x7F030104,0x7F030105,0x7F030109,0x7F03010A,0x7F03010B,0x7F03010C,0x7F03010D,0x7F03010E,0x7F03010F,0x7F03013E,0x7F03013F,0x7F030140,0x7F030141,0x7F030145,0x7F030146,0x7F030147,0x7F030148,0x7F0301E0,0x7F0301E1,0x7F0301E2,0x7F0301E3,0x7F0301E4,0x7F0301E5,0x7F0301E6,0x7F0301E7,0x7F0301EC,0x7F0301ED,0x7F0301EE,0x7F030209,0x7F03020C,0x7F030222,0x7F030223,0x7F030224,0x7F030225,0x7F030226,0x7F030238,0x7F030239,0x7F03023A } + // aapt resource value: { 0x101000E,0x101009A,0x1010150,0x7F030055,0x7F030056,0x7F030057,0x7F030058,0x7F030059,0x7F03005A,0x7F03005B,0x7F03005C,0x7F03005D,0x7F03005E,0x7F03005F,0x7F0300D9,0x7F0300DA,0x7F0300DB,0x7F0300DC,0x7F0300DD,0x7F0300DE,0x7F030100,0x7F030101,0x7F030102,0x7F030103,0x7F030104,0x7F030105,0x7F030109,0x7F03010A,0x7F03010B,0x7F03010C,0x7F03010D,0x7F03010E,0x7F03010F,0x7F03013F,0x7F030140,0x7F030141,0x7F030142,0x7F030146,0x7F030147,0x7F030148,0x7F030149,0x7F0301E1,0x7F0301E2,0x7F0301E3,0x7F0301E4,0x7F0301E5,0x7F0301E6,0x7F0301E7,0x7F0301E8,0x7F0301ED,0x7F0301EE,0x7F0301EF,0x7F03020A,0x7F03020D,0x7F030223,0x7F030224,0x7F030225,0x7F030226,0x7F030227,0x7F030239,0x7F03023A,0x7F03023B } public static int[] TextInputLayout = new int[] { 16842766, 16842906, @@ -24416,15 +24429,14 @@ namespace TINK.Droid 2130903309, 2130903310, 2130903311, - 2130903358, 2130903359, 2130903360, 2130903361, - 2130903365, + 2130903362, 2130903366, 2130903367, 2130903368, - 2130903520, + 2130903369, 2130903521, 2130903522, 2130903523, @@ -24432,19 +24444,20 @@ namespace TINK.Droid 2130903525, 2130903526, 2130903527, - 2130903532, + 2130903528, 2130903533, 2130903534, - 2130903561, - 2130903564, - 2130903586, + 2130903535, + 2130903562, + 2130903565, 2130903587, 2130903588, 2130903589, 2130903590, - 2130903608, + 2130903591, 2130903609, - 2130903610}; + 2130903610, + 2130903611}; // aapt resource value: 0 public const int TextInputLayout_android_enabled = 0; @@ -24647,7 +24660,7 @@ namespace TINK.Droid // aapt resource value: 2 public const int ThemeEnforcement_enforceTextAppearance = 2; - // aapt resource value: { 0x10100AF,0x1010140,0x7F030066,0x7F0300A2,0x7F0300A3,0x7F0300C0,0x7F0300C1,0x7F0300C2,0x7F0300C3,0x7F0300C4,0x7F0300C5,0x7F03019F,0x7F0301A0,0x7F0301B7,0x7F0301CC,0x7F0301CF,0x7F0301D0,0x7F0301EA,0x7F030234,0x7F030235,0x7F030236,0x7F03028A,0x7F03028C,0x7F03028D,0x7F03028E,0x7F03028F,0x7F030290,0x7F030291,0x7F030292,0x7F030293 } + // aapt resource value: { 0x10100AF,0x1010140,0x7F030066,0x7F0300A2,0x7F0300A3,0x7F0300C0,0x7F0300C1,0x7F0300C2,0x7F0300C3,0x7F0300C4,0x7F0300C5,0x7F0301A0,0x7F0301A1,0x7F0301B8,0x7F0301CD,0x7F0301D0,0x7F0301D1,0x7F0301EB,0x7F030235,0x7F030236,0x7F030237,0x7F03028B,0x7F03028D,0x7F03028E,0x7F03028F,0x7F030290,0x7F030291,0x7F030292,0x7F030293,0x7F030294 } public static int[] Toolbar = new int[] { 16842927, 16843072, @@ -24660,25 +24673,25 @@ namespace TINK.Droid 2130903235, 2130903236, 2130903237, - 2130903455, 2130903456, - 2130903479, - 2130903500, - 2130903503, + 2130903457, + 2130903480, + 2130903501, 2130903504, - 2130903530, - 2130903604, + 2130903505, + 2130903531, 2130903605, 2130903606, - 2130903690, - 2130903692, + 2130903607, + 2130903691, 2130903693, 2130903694, 2130903695, 2130903696, 2130903697, 2130903698, - 2130903699}; + 2130903699, + 2130903700}; // aapt resource value: 0 public const int Toolbar_android_gravity = 0; @@ -24801,13 +24814,13 @@ namespace TINK.Droid // aapt resource value: 6 public const int Tooltip_backgroundTint = 6; - // aapt resource value: { 0x1010000,0x10100DA,0x7F0301D8,0x7F0301DB,0x7F030279 } + // aapt resource value: { 0x1010000,0x10100DA,0x7F0301D9,0x7F0301DC,0x7F03027A } public static int[] View = new int[] { 16842752, 16842970, - 2130903512, - 2130903515, - 2130903673}; + 2130903513, + 2130903516, + 2130903674}; // aapt resource value: { 0x10100D4,0x7F03003E,0x7F03003F } public static int[] ViewBackgroundHelper = new int[] { diff --git a/TINK/TINK.Android/Sharee.Android.csproj b/TINK/TINK.Android/TINK.Android.csproj similarity index 97% rename from TINK/TINK.Android/Sharee.Android.csproj rename to TINK/TINK.Android/TINK.Android.csproj index 8051c03..314d158 100644 --- a/TINK/TINK.Android/Sharee.Android.csproj +++ b/TINK/TINK.Android/TINK.Android.csproj @@ -30,7 +30,7 @@ full false bin\Debug\ - TRACE;DEBUG;USESHELL + TRACE;DEBUG;USEFLYOUT prompt 0 true @@ -49,7 +49,7 @@ pdbonly true bin\Release\ - TRACE + TRACE;USEFLYOUT prompt 4 False @@ -63,16 +63,16 @@ - + - 1.3.0 + 1.5.2 - 1.3.0 + 1.5.2 - + @@ -161,11 +161,17 @@ + + 1.5.0 + - 1.2.1 + 1.2.3 - 1.0.0.6 + 1.0.0.7 + + + 1.2.1 @@ -182,7 +188,7 @@ - + diff --git a/TINK/TINK.UWP/App.xaml b/TINK/TINK.UWP/App.xaml deleted file mode 100644 index 662aeea..0000000 --- a/TINK/TINK.UWP/App.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/TINK/TINK.UWP/App.xaml.cs b/TINK/TINK.UWP/App.xaml.cs deleted file mode 100644 index 538a8f1..0000000 --- a/TINK/TINK.UWP/App.xaml.cs +++ /dev/null @@ -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 -{ - /// - /// Provides application-specific behavior to supplement the default Application class. - /// - sealed partial class App : Application - { - /// - /// 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(). - /// - public App() - { - this.InitializeComponent(); - this.Suspending += OnSuspending; - } - - /// - /// 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. - /// - /// Details about the launch request and process. - 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(); - } - - /// - /// Invoked when Navigation to a certain page fails - /// - /// The Frame which failed navigation - /// Details about the navigation failure - void OnNavigationFailed(object sender, NavigationFailedEventArgs e) - { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); - } - - /// - /// 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. - /// - /// The source of the suspend request. - /// Details about the suspend request. - private void OnSuspending(object sender, SuspendingEventArgs e) - { - var deferral = e.SuspendingOperation.GetDeferral(); - //TODO: Save application state and stop any background activity - deferral.Complete(); - } - } -} diff --git a/TINK/TINK.UWP/Assets/LockScreenLogo.scale-100.png b/TINK/TINK.UWP/Assets/LockScreenLogo.scale-100.png deleted file mode 100644 index 2691ddda5679ccd76bd853233c0adac58bd36c79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 261 zcmV+g0s8)lP)Fn|k z&oEl$p4(}|X3XH1SD1VLg3PR#bDJA#UZQvNAu3eIx^d*aYRD%%^sb-js+$Et|+eW&YWy>as4geSy-A=VkX^k47Wv%wzP$Cgpot!M1S75IDB3*H9LSdLiDS^a(H%NRBR@VF z+}J(cN@R!RYzJFP6oDpKHxj+4dD};y@{-2)cqxY7UOxk5JRpccW}RJ=4D`&!7?8Fb zUlol{=Dk%DDl#Xt*VGmC?8l`*w7B_hH5nI=$KNn;YgKzctk^^%!s!RP+l6c%_I*!*B<)9Iket|MrkF zuY$u}4byANJp}VeT099L*S$&)JPGRKeXV;Ir6(5Ux#YOB5BnvvUbFSoz|vIj0roo3 zf{(KLsP=$|HAW2PWK~MfHl2mwsG8kiHaj601sZInXWuTAfip>7;}|$hFsL*RLRs3D zTIh2f`m?|kZs>!)RrJoST9?-o7O#^%;5C+Cle@Op|1S@arXJv0kHD^@;Cja1UJw84 Z;to!Qh;-Y(HAesd002ovPDHLkV1h}o$6o*d diff --git a/TINK/TINK.UWP/Assets/LockScreenLogo.scale-400.png b/TINK/TINK.UWP/Assets/LockScreenLogo.scale-400.png deleted file mode 100644 index 2fd2f99492dfb22300e310f97dceedbfab5f233c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 758 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#_0(?STf%JdM07v|#HlQh@B|(0{ z3<(PN1>Wy>XsDmR;Jm?vg8uaZ`4=8sD9vGDVDj^HaSW-5dpq-X-y;J7m)p~C=y)m| z?)m>eJX~sC*w(qi$%-E*eD0dHW74a2YB%K6)z!E9ycG!MyymaBwmExS+_Y;)FIBhN zubtdoDj2OFyhU!^+S#ucFf8k{OALbdR0a7v*otDq%4QmfNgREWJfT z_&AF}>V-uVQ$%L+3;*3_JvM~phx4O(WC%MO$AyG|?H3QM&|=t~YdLRA>a{Ov z%B63%2DZ1H_ZE++);mylnc-T`W7RWxy&S(L?3Xs2{nPp4`SnE+?;Bs*e!cv-R`R)UK}>d?YH#K{Bb(l5)7kjA4YYL}j^DV(5*uJ@TXAyl zBFjab(!8ANj~5)5S$*kU!~%^&uROf6s_XD#Di1efOLIM;16BJS1Ab# z7;Jgw`dsiM^3relK3e7 yZ(w?*WVPnGC+m&}Z2BtP_y2BmZu{iPllSMuRhMpmm}&w{dJLYfelF{r5}E*19dkGU diff --git a/TINK/TINK.UWP/Assets/SplashScreen.scale-100.png b/TINK/TINK.UWP/Assets/SplashScreen.scale-100.png deleted file mode 100644 index 8fc01d548a8329a9d8f661633e54a69199e933bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1706 zcmbtUeK?bA7@r)?m21Z3E5s&sosr?>n`SK^qpa2xDw(#U&??&MOv5xChMdudoJKfB z3e{Vzg=juYCl<*vC#-x6qg2Co-nq{I{c)Z@p67m^`*;8D`?;Ur`(_Y#<6-N})&<_$ zjx$%KJelLXZ^O!ndMszseXY7w|3$4HoOO?SKOe!gX$LU!^oio9PmqhzQK1hEYc`IM zt2kNM^=;UbC@#kHy1t_Y7u#Z)-OI6Ox;d?(z}4{fGTS@6SMTKMlAAS$MWi>GS1i@Bk`HHviL;ipKK8QP-87-(!<|M&y>dzU z`eLLfbEsD7C@)#|UA9_eMv~up_s{Hg5E|Fc=fcvO`=L@|D}uuNx$e=})zp!|5(VdCNVZ;jkvc%UX+$6C>##11>diDCW$Vxo+lmUy2Wr!R^*Y2ab zjM(FxE)n(;3<$YZJ9KUSvWFrumYap){3eTv&#qFNO;`mhgh~f9S)&AmtnN!C3XmU} zTwS@n{hY48&3Mw;m&ZcEmLeSln_XPhy?bB~7D9Eh9_K*i z_=2%au=gOk97w3nz;N)=GabiiX0p7$5&(1-rZRv46&09%d}1b}@Q_OFSkSdfUJm@J z9|Q&CxR@3&3oSUIYhhp;sjQ+jT957P;<|+d;E6PySegvJYOKDD^|!<1oUvFoW3u)O z;V3 znd~gQCHJ&b7}3k>kq7~_s1jO)MF>Ks|?|$8m>Vj$anQYJ#p0Z3TklFIbeag)T zr1Q*2$Ty$J%KaFnRnv(NXL^}v6{LZ-w3W3K1)QEyp?6v)jP&)Eg${dOASG-SK8_kJ zu6buY*w0}dDk~AO2P`yW&UOykxF?HI{Y42Ck`MWaS8pd`ZifrbHM|)#-a>%B)jt1q zu%QSpa@s#_rQ~*guh?_=_o-|9WxS#9r;|692phwvMKvdhRQ@FcO#*bqi$mzHOBgY~$g1W%2^ zH9X#Yv<(N+O|8m-!n~sO>)j0%JnTH{&_fD;;+fTzcOL9OfG~Z;?|R{aiLunquNA?I zrM=5I4?Y8)NM#{Wf(*>x+Ajo7YNG{#%SH>=xrH5EW(h$_{?zIUer-%5RVVAje*vRt zW)%evlSdg0qZ@VF7{yi%4Q+YGV6ie@aa*2=lp$(G;HLErr9n_L8A#^<@>T{kt95!H z^I&dT!i<6!=-C_1Ol*L7~08eye>jR$&o zDt{wZSgzohjjb|z4~UBHU_|;y-!pb@G4}u`snEo^rLTK&$ZoOt>TwqfaR+W1$Cjtt zyPNcAyh_Z(M2~nL$m}LBm+0&^Vu?HaR=pD`Aj;|3uDyyZu!zKb-RPsnEU_BYStm(V zf-;A9Qs9c?T)wV57c=gq{B|FybhLO-x(9%+R0W{@Jf=83K_K76#W)*gZoh^2SqyD- zUk?n8 RyFIIh2m0;yy|*Kh^DnK+Oi};< diff --git a/TINK/TINK.UWP/Assets/SplashScreen.scale-125.png b/TINK/TINK.UWP/Assets/SplashScreen.scale-125.png deleted file mode 100644 index ff8f8c30ca2125e715c7b08d0133d6acf989703e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2148 zcmbuAdpy(oAIFD@Tw;lub7V#x`tg%(em}=B*5=Zgp;1U}VQcP+v*ZHqIM@7wjXZzj$JLjL@=b!iE@xDCYugClSc|6|Vg^~kI zjjW9z5Qu3|AYl&#qGtes=o~ZD(|8_YA69A%K9m$r)L8ZZ_YA26g&K-g<{mNyf-q07 zd#-+Th34CN?sV=fKeT*j+GFS?ox>1_!R;V|UwD>|aw2T+_-=FPV-BY$Qa4{OO}+n= z@uZ`HwGd;gN=2*{mERWi4i;&J{?S92{ZT~gujY=WPkzL>dQUH%yfEfLn^^j~c4S>W zPG4tyhz4<8jk%&s-kGwRJR$~-L_&Vr2gIb?+fz5M&2hnB+wYjdBR|ISKfKt`T|MbzUjJ~8^Vw5ooeziWwm{Sl|Qj;(i^Z_VI93FYXsb5Y4B=4&42u!VvkMP|5D){lop%OMS6 zK&0^Qc-M<|tTF@1(JJa`}HIj8yzV|7SmIwMTGh+iUA8tr9 z@DAngsBvhwz5Q+_Td{8`A?%66+b1Ol&ERS3@m$Xcj4Z0`And>{?cnkh*VZiKd$(Q` zGuJuBSxE-Fute5wX5i&c^MksrdX~YuK!TvCg$lm`o-Eleua+UZo8NnugB!PSE54O4 z0oP`DF^uS3yC&IKUh($D2ZC6bT{#eqHVW-&+6>bTU`i+&!o{NUCYwg)(a%EL>4Plm zMw|Emx{0jd@mEpUa*!({sk|$rQ@>e_6Ej~s#Z?_1*e_v~+g5Ubs$#=}vAh}HO8Gw= zkx=cDB|t)Z(ay8Dy*BddPBrYjuhN$l-NU`vxlpU^pkWyk*(9Ye5$y}V+A+BZZ?ut; z*PF=-ka!8JP7vgScCmsPQCf=`g(yZ{k-3yaZy*%D!UryLJneWtQ=u?Mpx9_-JDKZC!gkncz5I__1V7q)~DC4d>F~OJC5nLc20h?GI7e4%9~6ItGVx(?nY)^$0y3mlsv`! zg;nSkJ^V1b>~Y6vNKw0x)_*L$D($lcn|MElyyks7`R^HTT=Z4*uxO=#{mWS^!zMSc zS-J?<;dr;4)MX9&H*^!%qbRypQK1Q_UA-l(iq#UsSQ`+J(5ehg3o!?fYU3p==?(hsO+Afg`Y;2iA5BkDh z+=N{cWd9|3y?(JFCEqqXXm|sj$i4}4z=AnBY%52kW1PR3*%4o^f8eUniuV_<(mzrevcS^*Sh5CNbtUZ+8$=x2?ktgPsCxtc+tH*Phx6^ zT~qg~LDj>h2ZKm-UjFkTysL`x#sGvROx%V5CCpAI-lD7#QQYEYE;26?rVtgW`@9;yUT+ehCG z`w}w}OMQtHMzOm+ofM8SmHn5E#JW4g^#wAD!$xCa$rC}Sn5y62LPWw7CbV0hucmC( z%RIV<+0u3G(_C^=zFX6y_7g_5Ti)uCt5u8d2526XJ%4sk|LmMX5U$Exi`0`|xPz8w OwTVGQGT}0wcIJ0H*X5P~ diff --git a/TINK/TINK.UWP/Assets/SplashScreen.scale-150.png b/TINK/TINK.UWP/Assets/SplashScreen.scale-150.png deleted file mode 100644 index edce169745fbd0c650fcdba3281a5c2225c16588..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2609 zcmb_e{XdiIA78W8;>g3sI!MZQohi0di%MBEbeM2($jstPB1}?B%)O{s^mv*xQXVQ5 za*ot!wR>ef2`TceEO$dmPbSe=oa@$~@ICi0_w~9ypX>eb`F!sCb>;f`dK&6m=wmP# z!;M}W0x%f8G7M(MZGs-yxy{bL3tn!1K7lOop8EeDLd25;0L6S~fbVY@8{W~g@+`ll z%+FIjXN->s+W1$J9$$a-;4ua>`_RS>>jTqfd{jYsgZE8mBECKy73o&F{BB2tAQYhf+t0wArPk;7?0%}*NrUC~2kUp0XV~MvIkwg>Pq2rl z9ID@Z@zi$|x4pP{q%>=R|7BI2`<|5As`{w+4KjvvXRAYhzG6@HjFNI>ZN~)lRR&vi67 zP2JoOoT@-s=f=^di4+DgEL7!xV(}<&4i_sPWlXkrRZo`f(ba7;leq}tbB5ae7d%5B zgboc8u;O!GNeeAD%wBWQ(=G=Bd%l3jSAykyHJm1Jr?(L2L}d# zC-I19chuN0GhK|}4`df7Kj2>e4sxJo;;N`OQ9B<}TUE^>h!P#X)k~jKG8JMX&l>-s#C?mi@Vkx#-8r&NP!}XpH<71+{Rp2`@1&aQHC>_ z8uWNbYYV;LZd_S#YO*0kU}{P`@iyaRHgEZr zMT^q_;sB2e!j2c7IO1q)NN8$eg$;i;=fhz*uCtIOSGC&Tw->{tRDhi%KleBMUr;M7f)Ae z+5l=rqmIo4GYr(w3qHx~5fA+gZOuhBc}*_^^bJQ#zIRXIEdzQLRaCjNB!c*LE(*$I<_;S-bn7dSI;}e!+k3 zYj3)<$%BO-BZ#SE651n%zsz~BkI{~BFU4?#%(+p(?l84EU74#^wx_XIv~T96GyHW! zsj@|qi}2{+I_o3b>5w;MNv*wEe?E8qvTy4?-3aa5MCvWO$Ev%RQ}1L!R^0rg*4!V> z^@dhrcJJ@ZiA&uwKLoSyR_sbwTGhwbfxL9_r)S}tG!d(JUL;y(X^q;=r_S98y#Dtr z>8?>1i!+U+%7NjP(yG&V+NOjTJsEAvs1+?q#pJN`aSr4ouD<|N_;&m$8*)JS#EZ?} zG=HgPiFzYLus?A|!x|e(dmd);-Ywy49g}u9K6WNQk444^Vx5tB-9pab;VJ7aFKe6^ z^A%0hT5I{X&nz_0M%m9I_-i%D%nN+r? zKy$j&4LBvUxxPt6DRVM%@6Z?_N!!yloh0{^rX-JqoHY_ZW3fl>omct;MhnX_ldUp9 zxPIvP&(aSOR^Kl$T~C!=#;Q)c zFB1*%ROzv0%BLkdG=o_F&1a>28f;mPo;WSJql(}Z@HbYRjYbvTcb~asCU-DMIJ$%N zpG~52EV`fy#uMBv$mzqD|LG;ho7|!L>>?1cYE!?Hm`J9i+;JDWzyx zFD3577P&6CRxCm&Tq2uVpwHpkQc4#|g00M?yvUJ1QXchL2}TV##c+U0VWLv18vl{)5@vf|^p7ETsSF z&r+8<`H;q=ZegMNujs*Z6YfJPOhg!@n*Q~31nnQ=b>+QrtH=uPDI8!Q{{_rXWfYc- zqhD!5Td>-R#4lcVCY}mneL9zYMP}=}60zcWSxB^#pyGg0&q0MBM-)BQUFYr zrCkh*Pn2dMN(SkWq$Ll|CqV`q!X4fUgTlM~Y~o1LorW=x0fGA{&nC|F2cvN+phlEc zgfeq9!f}Tx>tKAhmx&Y}Rfy1);}T&JN?cRQ?ARWROROMpYzBebA3Ie345Dq_!b294 zn`I^7u5dc+;1hZ+%wMd+wzYrdz!Q~Yj|&t_JALQT&XIWA>)jLVZej%$UqUi5%^h)0 u&+Y_d309pccq+T`Zt0wJ2LJZ`2jJ<9EX#G}E$dT-DELN}?}i#Tp70+gqNMcz diff --git a/TINK/TINK.UWP/Assets/SplashScreen.scale-200.png b/TINK/TINK.UWP/Assets/SplashScreen.scale-200.png deleted file mode 100644 index 027589c778094e5c7558fa569fe0be3573e2b88f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3566 zcmd57u_E!~2H_zUkn!>MEgK_}_P;PO%oV-G+VKK4b z{7-kpz8d1USCEI_;j0P}0_-zeg>qdjDnbi(CV@T+HY= z-M1kMdwZk^IaeFuca*&P1rC|)i13S_at=oHd*T|0<+%>geod?Cm5z@W=o7AYEX}Y9 zrykv%fCaY$cpnxyv_!=v-k5B}smi+#qHmNELP>f>LjRuqi?~-i_ymI8(xWw;7mHB` z23tyz^m18G%?HG9O5=X5LO9N4LL}+4$7Oj)e);7KwL4H))9gEoRk~bXV-+&w91%}a zNz(E|Hlyg24LkzDrmSg;r)#Pg27DA|Brhmi(f{xRH(A^WO3LqAJ~z7GU`FY+LM(T+Jz>X!tF40N`x9k;bw7F5_A@qXzG1hjPUgq zES&$nBeqxglL!r`TSiiTfe38@6xRH@(f}wSj5UIqfq5HLk<7nAB>5Y(5lw*}&Z86L|3vWQlQI&jbfK5~W zl|H%FoMbEIlS^t+e(L1dUNU+cTvD%O`^85pXy=2Zf>hwOsyHj}6oGvRWX2GCH4H!U zi8PY0ifD62Rp+Oi3XS~)wx??j1}`_tM-ml^V1K|@sw$J0M17kG=1^XH$yd6zMzwsL z^}0rgpExnpWptiU!BiKNZ!GT$rBpv#X;6L0ieD4U;n8-19HyLpXw{@!Go)gw)_HJv zyVUn|?o{)Gi~LnE*=$8)y_^sA1-(VRSvicp?J%k`R$tQA%=Zt2*xdWtn)N1hA~LFK zXZB%#iO#v@er`436XXSuF0vTV?3AS2 z$Fe+>*CHZwQ+fzo3>%5~r9Cq!i>u$id)uDA?t?9KY9Nk`kCByFd!_gpg=pT^(tTBw zhEGxl`>i#VH7$nAOTN|k?`;JuE9{cdh8EK+>=Ct&__DnXAW59!Ma>yYk>s&E9#9i7 z=2Lf0{;mkkT4Z=@^?DZEF`ynx3F^W1>N?f>%-yRz*uPg8o6-7w8A*bBj1B{werGI1 zRemzYN1zZ0-ZdsOw#8D2Qup4>5;We5v-&Lz+9dVyohd|Q2B+6|q3M(n4LjojX*cZ4 zl!Uf*%B&ep^|9L@+yrUhlfbMsa8^Ekn8;M?Hbdw_HQQoS%KtH=b&SDWMuNNatD@mB zJ9rB836^v&wLN+f5;O;opFVA+y+veB9gcu~H$`NUH8IesSt34h0B$HikkOj2RF*?8 zc%0&Lqt#Z`{^&d>iJmOJu6Kyc+y(hS54i0*0ynUOB_t@~kzDX} zxeRRiG|)N7@UpgUpyz6kgry9G<`1^C*(*+c#QvlrN7l}j`ed_e{mxxM&b3|pVsORa zP0mzI?VR7fyUzKfCn-JTxZ^iGr+JFmAF-1&PjWfG0v%zL5r;gS{?i{oF43~~&Ox+( zrxD~Sw3pFJgu7kVE{~ZcCj|dO%wA&yHi1FW)XXb zo2L1!BSzQ#bF8SxnXQkmjS75Oi>AWU_J*a?H;2Y{O=6t%hI{@>fibERT}Ns5I!?oP z^fa_fPatzDG!p@|xSV;B{0Vh}fNXxVcj4jQlf;Ss#HH6NzicYG-MHqo&%SAF+Fqq? zZ(ikua>&JQlO%_!drL};{9^K|DOQzwzTCMPFH)Q@PyL!Rzz~<->$p$8eS+IIrZe73 z_BlSZ)*gF{mb$c=z`0WMwqEzLEH6sGvOVgqM&B~+HAh*w*JXO>AG$`3*SBb_3!Ty& zLZwzr&vPOkBWcE-yxK-=n9Wpv;>%8%Oyl&uB`xL-MfVqTM%Gk*j(EDpALFI8AC<;0 ze<(p+`DaYa+eNkt6Ul^(89l*_qwDHC2T<9rcQ9AGg+y5)Mk+WMMp26+PNX!Uxb06# zer*B!D&%}b?o@m8zT~+AB5qMlwxZSz*ZGKRsa=n1U#^xYvv*2bDTwa><)E})ho(~# zgz=nA7U`AaY>9XccNRv??x3zm(vKWqh$j@g7`Sk|V+peMebNw|Go7j?@NlBmQPoC| zJ6e0DTU|^)%G)p)MtRO6F>pllgGV<{so8vej^nfF^y^JfWr4`mX59TREE0uFG#}>l zFTg4;@Kb}6Mo5XL;y#9~5o+Trt~jHWPEmE`Ubkm%vR+)6kN@6E&~uDuwDKQbrFjph z=o6*|CpGAdgOfu|VU}VeI(K}GuL+Ter{X!}pD6=E+G43-m7y?*T$hi*dK>BwwOAyE z+dY!=52gbL_qyDlg#;vzr*QkLFj(ideV=z64J2C)nHl0FYd9lUz+RW({R>1r93I%~ z`S={i?9;CrEF!c)IaR;xgg{_nnX=9btl=G5$C*o~LObrmO2&+W?F{jy89Tqn)!ALG zvBZ^qhE;2CHTL#*FZN8!WKUP~GUb$&UA$SnW#T=lKngQ#gMT&?kI@+LlUx{d0%ja; zN1Melc!@xofga@dHRl#ubD7Y&dsw3|w?CaRx6_wxwysOdq5a(zR|NV7Veo|1Y5qP( zDmODVuQi^NY>lBBJnMkYfjM;AEK`cCM!e{h5^E&*&hG9EOIwg>nscr(0cX=u2fjS{ zetds16+}90OEgQHUA`}u;3%&%QnfY79qFrhc>%pb+N=n5;$HBN!BR?Kuh0s=Qr>NL zwkrrn zi{bUz7l?e%VpD_t;i^4b0mA{v2=$u_e6ZX2`sk5+D@CuAeGof5{iA$!G4Y;9k32 zad!9Ymqn#Jo-{5|X%jlmr=vniJilE26#Anb#~>m#Y(Nm)?b_N}2?ZfuG^`5(f}#6*#6A?#8l)!r4!C54PK?JkrwB1A@XE~HawGU}mCG9U)%aiZzx$Kg!aa^wX6$oya)apxKFmus zuLner^DTqaGXkSr;#tkE7MBwNG;-{%SGtFjdSC6_R=j-r@h1vecH33Y6p3zjSdrw|4aOqsogm}+0T<-ACA~~!}-f~3E3RQa`5Qh_6KeXjkY?rq}~7?UsWN$%ZQB?+9G)RaGV?w12*luP=i-})0} z!RN+<3g>0Oe3?MWBuorHG@{;pP67Ug#d^OEhphu+d-!OHV9$79yh0e;K^C$x}2vts~$&PTlQLQX$Gfr(`s@!ceU7H|aBU5n#~0JP}D zr>={P6L2Q&WNhE#i@%7Dd-l1vP~_ z`*pzI{WaNSp)|&0a7vE|t`~(?S}Zq-;T#2mtd-}lCgBZ?o%0ra<-w9msY0zSs z(EOWV$e_%Kc$`2OOIh(o4D{YINig_F`;3^0NsHfdH<5qXWU@Z!{d%ER{(~B))Kg5DU-_gXa39maKs~}m0}w{(|&SVD;|ghcLVfaX*oLD z|KFikf2;R+Dk6;+G98IjJhdk-kP5;yf{>ieY8-KXp4xMAKoY!^{)=o!##Cnf?!iM)H{xX1%k48F!?UUwJ&x$tvR6p#&t{o)z@+sMU})$G9r*Xn`|u z<~5l-o2WH5H407|+C@d-Gc67Ic<}2lV3K5#?4A-45EnW( z1#PQRV;%kGJ4P(6{+uj!Rj1ph@F%AmD|ImD{m6zLS!~<8M+>kbExFs16wdGHzom;@ zT5>ht9_q^6=UdY!|6Lzrs@B)(JL6{7s@Qx1m`%eAa^Bp1N*n%~U!5%;yhb+aoEAOe z))z@QUOk>$=gr-&A+9Da?Gx1>v)HH8vQ2Y6x6}J_njaY=X4j|`9?TBi+f!oz22pk= zt|g~;@yv+&R~KmX6$bv}rpQ}D+Dz7>w}eDxvrKc-m0)^y?kxUQ;Z=71oy5y%Q2qUp z#Esmn&K195!P_xZ3hKsM$&jPSDyiS#*Yj;1n&z3jkE$*Ecs=#w%qj3WNBco~Hid!L zr!W7ej&R=Bd7v6hc63DltHm;~RBFXO^KFk3XyPyw%*KLJgfY9Ez%rJ9q$HYjUR>I_ zJ>9cJTV@?Jnt_^+%;Bgx`1ClBANvwdCWKU{oLin$u(wy?Zyd}si67z_zl!dA=QIauUji;xS!;!f2Z+n9u#7nzNC+U1#TBtWm;OqBw#Nm8Ko)%}OlkR?hQ|g(OaWnxo~1$}OY`Q*lnH_h)=rzDJ-mdN6n%73u5y^HD$o zP2r*vVsFX#QfJ`XZSw|GHGD=?lPG;R=i_GEy-1Q4eauuogcTm7B}%C(CYWt8a*W{N zl&Fd`Sz0BRmX8^HH{;9Z?+Xu%ugu)+1MlyZ`Zrb_W7RaB?)o$=M%w95c!f0CT z_XYIf{kOwLD>77&#eH+SZza$Y@w>m8@XeXFU$yCTBeXCt-nZ);8Sd_GGXrM1Y4YpL z(V+J|yRQ)gwdgDMPW-k8MS7)#TnYQmwN=_j>~jx1ArC^@jGmT;cv;>LPW$h!;6A(V z_h{SSQ}MZmE$f{bY7H)1f*j71q3SdFOUgzrG3WWKNo3TVB2+^q3;!}5Np!AE%PiZZ zM*>z%T1~qaef>Kk2a;#nUzJN_M~lm_*6FIZEN|5h%QGr#R6U0~2<*3fr7jHRu5dj& z1kBa?86PK6J@mx|#-1xn;vk?TnpZ}UU#{YmX@kbUyoA{ zGmfI4chRb(^q%AQrc5;?z+d5cUbU|AoPuBnXwKW5Id1rosFp&KPbX)_8v}b3v~~(M zBfUFUNiWM74B#uQ^JP7mK{RKdPr6qJTO++S1aH>pqj5HswLr7%?j}!8iv--PR3?}C z&Htf3UpA^SL#O?vX`&+o3BBFEqEA6bu+oVHoN0YiPv>1c&$b=J#EutE|L|}M_U7{v z7^^SqMXI^*Hv`l(w}-Ygq;EDZ&OAI!^Oi)fi8Yu9@680awM#?3y%_z+(YMh|BrfVR z6d7LimpKy9F0C-f`*oENrx@n2+?G?kBDQVs;$MigJoaM6ABHK8((({`EXJ3W=op9v zY!G>3^W*Dc$*VKi(K9A^w^Cp7x#vTqsUWlE&^2$fq^PG_0;2Ri%%>?lrEMVs%_I>$ z^t_wd@SPe98%bOoYL@N3kBe04X)tsw$XRX|mQyViDJ-dPTK}Nxow71`Ia+_Hq&9k? z(?jv>=$?6D1F|QSF+mH0`IQg4wUeS>o9xs073E>mi%2i;px_rA@R7bSSDczJZ3y|U zVdKA%dpAZF()@KkPn1)ahIF!cz+tX!>(qLwh?2p2>t`WX{T+`gsDg(=(m|7w!0zRV z#s*qo;PcV*(VJE+!IO74jUjxsdt2wD)G^z&Q{kFgWLA3B$f6HAW3|AyX^Gjh7(0VT z9qwJ;827rFW$g$EtK#4t_qOe=6GoElC&+x85{<9zR-@U+jg=OBwNI}-hRks}6mJ}R`cI9%%(Q8s->~Vy{-j@=zP2+d%b*}sU2x_z z;Sv?)9H3cPLD^@6q=y!VC|rxAV4(0LzOH&31J=AwCTptHY&TSic7GIYa=nXP#=V z#jMs`8~M=Z=%^nwd8#au#Mj zo?fST6C`_iVAFF~E)OfVy+31_99&8k_b4qo8IZ0WHzES0SS5fA*t23xO|6qxuxbcrpq!8;NRx>i+RU-Q)qkq={TbQ}2#we@t3wl}h0OYi00Pm|%t5v+Ayu zx85_|1JK1ii@u`Sw)=o**4*&><0PTfR570WS(#T^%A`vn&-T3dnEq*CH)EcF1jH&P zy2CeUVEk`eGL9?%Q`Dn4Ub1AwP63v8Ddl|I)0UR@A~ln2IbefBGh*UaS`;M;#T3c= zvgU>Qx&hyPZga!tNpV|a_s+4+Jm|OM{tB#rf=8Y&>*lYEKv+AbofE2m^TKi)izTqH zzrRfPt(AgX>880DMU=|H)3i^bzHOyl(eG~nX#y5qe+d?KKOFN!wf;dIcVmtS>CL%!!#dAYfx?zxE_Rvhv}_w7zZr%v+=4AKG5^}QAUmnVnz~t1T@BL0X zFx{>!xWE^}1Ajx##$O9)dPzqq5YW(nIDtm`P+4pi2=8At3bnWW()q@!Zs>6s$+Bjz zuQOMZTeGdFNfi@ySN9ZWhPhr6W}mH=DGw*syNG6~i&4nyWX#$mWHYMiOK5=N;|}%F znWHL5dS={o8s{s~SC*zJrjpd<0AEI^^ho}hN$pv6+LmOKw9O+P z$U+}(C>$6q56-9od9Y_pM~L36u$IS6AMXzl&A~ah@pRWXh3S&5Z_ipy>m|Q50j5Ro zxRI#4sO}8+$(gZe60@Y9E@bLi(!S9fM3Fvwnu^+rHk;Y*g+X@i zj)1|E`r=`2AK-WAAE@2`@I|T@dYhP=5^&MY&?v=)SdQV{f-rU5o*69v1Eq-{YHoYQMK;M02G+dpB9kvvNx`rAj1PXZ@io4(9@t}0_rCK(vmXF~H*7hw%>X;b`fKczkH z$e2xmZk8_pwL?%yw68pN3bYW?z9WE9-_o=eftoK|gt)?&|G|nOV{h3J&|6Y}a}*&;jX|j zc4GoIfDtDIrFmC0Y)}(~%V7w@_HoGR>of}3605z*2lj*vi<+i<^&(Cg93pBblwadE zzlES}v)>h>!_QwW%lj*bo_;gxZ(MmE7`+|sDQK!5FR`U5Li6zcH{T5tX|db=dWZ%z zB&S8+Hpe5neH4u}5R2E_#(1`AHFc-VEb4l3o;sg&z}X)609CISk^r}|q>E(v$qLIF z5{(?VR$_b6Uk#WykK}fyq&b$~%e5k^F@EU26`m07s!!YcVZ3Z6iK}rmR0Bejyn=~+Nwe%hgJ*1;==^Lhv=UVzV>FqjOaaWSzg^5qf$1gX{$~1xc98rmM^Q;){e^oa2eO(H_8=5u>4C z{&6xn-IJHDqyf`;Edx&jL8fxd6AuSDqQC*Cc-7j&2B*r@)%ORv>!*Z>vYKuUbpAeX z;NqPr6ZyCMiD0nK^m$~c$X`gz^7lfW5E1j$75b&23NzrJ+kW>gK)AEG$;%H3lu;RK z>I^Da22LgBdk?3RZ;?woh9&M_%^NNd0fbT!IG8yxd=Q@QR5K#;UWreCOQ`q^Con0E zxHCD7WyiqNYmVU>(`5*Y8SUL@WhVUNR;x4t~I0ot@a~*u@S-9L}*{ zr-lr%=@lt>EFNBDNO zmT2QG4zGy-rQUbjF^_(n3~EaEywv~(rshvhtR|9p?=bFK21UZ;AUyhoAXnu{;r&i% zkRasFX+n)KqCh7H7HAPU@ai^y7hPWxV<+f2o0f71IsNJ1C-w<$gHFq$6#NTER*7S} zgeNcFoF(OX;B-P9)a@hJ ze@12AWJ*k47nJK1@P&$Wy_zbKUu%hU81I)GWXk@){hoM|&t7mkhCC@HMrK9`G?__U z+8Pq{@)E3wQh%>Um}Jw%0dDYMPIEw&c0N-$#erer(fQ-U*_~Ky8ntaoxGRSjBLdp}QZcB4Z!ptXE zRHg%ers|=56m{*GHw8kg%{d(agR!zTt@vW^T{F+&lq>{gKcp|hZrZqVojm5##amtR z9%yrH*9}v(Hd`4P7>tk0uB||;MTN9&UU<%MxyAIJBz${d!_Zlx3moTV&rJa)9&EKw z&(Vtp_-1_2J2A(@0$9fn8^Mt`U(X2kh12O+Zs^N=9#diiZdp_=5+n@RD sAN{~D?&SZ=7p4Di>))OM=FG2pUGrCo=#6r^1R3_Lovd%H^7-pO0PxD_$p8QV diff --git a/TINK/TINK.UWP/Assets/Square150x150Logo.scale-200.png b/TINK/TINK.UWP/Assets/Square150x150Logo.scale-200.png deleted file mode 100644 index e88ae8340efc1c2ac62f911f9740af706bc557ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1948 zcmbtVdpOgJ8~?J*!DiKQjoRc=CxqzR95sZW%OaQMh@~x;=4{LLVKQtjBSmY;aVskx z>BKRNE~g(e$I9j8veL|PNs-~Olq`IUao`#thOW(4`Q%ClI}vVp=%UqftAUo=wNa-~CX zyQvKP3pJ7wzo%S<-0iWaUwj`cVW(P^FLHB61h={MmJ_H_P>`$&o21&F3`SBO|D-En zo8X7Jqut_LG1i*ogq|D53U+;#W{()6&FUMwD)T4DG) zmxVn^Om&}P&IBs;?3NM6u`#l(ctm}{fxlB<*1%of9N)Ko@TZuZ5v=7BPKy3vvsV_L zWROj!Ce3>UMPRcI!i-)R!J3s9UunwkZg|^0b>FHhiyJSEtTC@wVZ|{7cNWf(V#~tqHQgON`<^cmy5k zr9Yz~*j!XMk7BNLbQFe$5oCc{x;*$3ewQL>_DY&?W7Vz##BCRPrxSH%s%}80!v5i} z99{gP`Lh-A_(U&^DvbLF$3vtz(De*U9F@^E2=gedX2)~JPfO&cXc@4k>%c=Soif7Y zmnjsI)fwZgAykXHtGMULY_9$Qix_wA9&K=W^391cg!o5B$moVz`wdqOw$2;5c9e`*WvLRNssKe&rfQd%*~0@W%m>cP1IDfj00#s@iOs zWtl@ql~<-m(_nxM7}Pxec(dH%atuv8wPYkrb2{q^>es8gKp+0ecfy(MXYPc&jZ9~g zJYr~4*`@v*#bA$;Kb?ojgvQ#$_H~dwZn*M57(C)c@n?F$5L-Cn;O3vEc>bwp+(Ah8 zu#Cg}idq>B?VRrvrxd|xXoLN30wP_SWFb`lFxWG)`Pzz0(1jc@$bJgE;7;o}W~V36 zr1=(}P*Ue4f7&Bp!7L`2~qQs~*b*F>@;q1HMhor7#utY+B&R07*JojPe4JNfcx zL*&||Z}ArN6g^Cpe{5HABRjYLX7wXLX1|x>y!{PAyJh=^NjTmU1pr8v*^fxDyLcN#>RC=pH$0wPYTUHG`! zhtzjB#7~d54NpuprB0U1Ehn=d8n-74n8?;tH7YR2s%%THn67;P^kad!j&ujle*Wno z3gd;uZ$joFyRtxaz1R)U9nVhgD!6;CdYKgIR78=S;@OvjjKY4V-Tg9*vXuS3zX{+N z*#MSc&=>fn;NcW;?}KPb?xn!K@fsI`cwPdPs>ET-f*b9XewdAM}ftiwSGRD9(@F>FAS1BMYlRTo6dfC0^D zY^!L&#|$ZfGBCXGCjA*7KWBdvneQ++o@pTPVqt)?52EWrK@)? zW?F67d~%_&W_3&}XU)(lhv1T9{T*f#r?SfAMEugms|0U&R>tY}Q6}W;R|DMFL+paT cN#CB@3$vBqp_R-a_*c!>E5P%&ql63p0dn=Nvj6}9 diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.scale-100.png b/TINK/TINK.UWP/Assets/Square44x44Logo.scale-100.png deleted file mode 100644 index 4ad2800b21e3c364d3626696b78c6522e0b1a164..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 394 zcmV;50d@X~P)m=GR+DVN8|{e0J| z+O<7_g8-ZYT(q`lkV<0>HE7hxjR%$SD7EcCD=RbhYGW_4J;5h({Mh_Q#sLg%d|egW z=^8VeA>Ii>>@v)30lpd?a(RSVZ#VSs(iet2YA`!acg*5P`$E6vp3$+zp)6QQkHV@w zGQuKeZe`>p)iTUd&+YW7W3uEOyA@{dy3V7$3#!*WGuivZ&c?CP*%vg%Ps3(@fWnvx z_7}I2ROtEYWNe6@m)y2Yb);O3+f2F7xaLxhMt))Bh@SCWMu*a6lqaqarD{nCd6e7q za&F??m-XC|v1zeyCFhA0^!d&nk)ZGC4Ppf69~ov}TBb*SOPe39e>Mw1ff_E03Z!Z5q#6&)aw9P zQhv{~8VF7&9RO3sv<=}wUI#$B4gS~F?{sw)13>^V6^46ca9RU`r~q4*iDP<@)m4B? z`e$V4Rs(`j0Um*{)zwNb7-n_bLu^&J%NjKWC+@G8zC}er5@t7IYdQ^;-4jn`g%sk8 zbkZl2FRe`Kwnqm^>kS{`+!(`>yW^U!Y>Li|7#5TI>bBEV3sfGkJ}hg|K|EvTP0OTx zd-O90nTh;@rry=vabK-Y)c_Xqi%kb<0pU2kO02>iNGGKMAc44dTAt{{7S+~-IaL%08e41S}Xtn002ovPDHLkV1kG4%&Y(a diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.scale-150.png b/TINK/TINK.UWP/Assets/Square44x44Logo.scale-150.png deleted file mode 100644 index 19ac2bf066713e327f77893911dc79f73312c44a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 563 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&|3?#2~eYgeU1o(uw0_p#h0gm`fZ9r4RN`m}? z85{~41or=5pK!lk;r;vx=K~hxU+6c;SQf~}z`)q$>Eakt!T5HXW7BE{0T=1kFs8b* z^UwTUZ{;cGv};1qa(`x3FyHzm@pu(9A8~0;krn?3=!#<;1q4 zz*8ErK_Oo6^ph_N|9P$F#A$PJ!E%lejpKz2V{}BN-lu%N!mMWYmVfQ}buMc=J|(Co ziEqB{tDQFa>YfG+~JsFoDj7^P^glLueSPF;VWJyrblwI7BNUR!qbL^d#7uaDm9x-fU+#cxc{ zPp5DuZNJ1F94*q75(t* z{KNIcTABK|RRcCoF8H%VwrRTYj*lG&Pr842$}eSVbX2q~VhS+c7(8A5T-G@yGywqb CeHEJk diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.scale-200.png b/TINK/TINK.UWP/Assets/Square44x44Logo.scale-200.png deleted file mode 100644 index a6e88ac3a1304031087e469df5728eb9b5bc945d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 658 zcmeAS@N?(olHy`uVBq!ia0vp^5g^RL3?$vW53m6#n*g5>S0MeLTyVoPLl~%ExFpCg zm?7c5!h3=J4h{7S=ASPJxRAfTzf77RD9iZC)5S5QBJS;si+zg?1X$i~+*sSFvite} z|JSEp@Y?x$>Gt#`9w(WOc^9nf$GuuC z+;4SS<#O3G+vRtJEt(UwHqh7WR;G*P$|&}Yj<+PQ# z+a1uG$N7$JQ&;29X*_}J-@TvsceiTWKSkda$F{MQJ8P%!vX;Kf8{)^`6J1pNr0hc8 z2i=9M9@<}L4%ezKe0r@gF@`HbjLBbvmrcd>kI1{*6DxyuL7T;H6$X{0g9nxa8o5O<8^YZ*M~K zdaj#GviS3Y6*jM*dO722fF*CJulDkbs}>d|W>sEHXO#`RtGqTQ#?-fa)w8Acfu?JL zA$IaqWaUfu%R7p`?c%@evCrRA+Pg~G&(Cik!(;7VbENWTuK`9sgQu&X%Q~loCID1# BIj;Z! diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.scale-400.png b/TINK/TINK.UWP/Assets/Square44x44Logo.scale-400.png deleted file mode 100644 index 0245755d3b5973dcbafd57d2c3960e77efce5dbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1152 zcmeAS@N?(olHy`uVBq!ia0vp^8$g(Y8A#5^6v+WnP60k4u0Z-fWq>38QX9||v63La zUqqdR&*e^|b~6 zV;{!6ww>G?doQ(SuthfMI8V{*5Z{vEVCvBE*m2r7)vdV;&7!0hY+TCNCMz0ipr4W1 z&^3+igM&A-$u^S%ZV(E{t8nyY4%uSTzz$Ii)x#El%l+!N2Y2t4iMAY6;BwFOFqhNZ z_kZ)dXw%G3Gc~`+H8uZ_O5r;HuJiQmOWBpyDQsJKU2pxWej2vQHL%<<>i1n;ZXS1w za^7V#t*3pee3!|;MU?Fs6XR#b=q?lfGmOz?4$-|QzyoG>&SNg-inV6vblXG+uM~dTwLbS+#$cy zSO&=NKR35AgL}fWZw2WJw-_?(9_Q^emdF)gVkqOABe;Oqsi1lXP@UX{+lS{q%qU*+ zvv>C{Kw*vxY{JjKX)G6fUT1iTsd@v@Sb>6u`Mq-?)-)K`+}$l+)3*4c+=HG3_ z*Mw&o#pxdXQ+;9@w&`8EBiFY@BKLwD>$i-WgGSQ(nhQU39_-}4up{=Tf9K^kDxbZ$>|8;$#U+{CM?4)8>M6E= zObs@)r%zmbw%p)`T!V?j?EXUT?-yh<`^?JQ-ZWK+HD9=9b?yMO+`OX_Th@vd{I|DX z>cPC_r}k5asmmgHoNjl|KV_Zq`}D+P8O`}JP5zpj^!M$%`0f5V$pixd7RLg;+bIg2 zhyVUx{qxAT9c7{Z61y5S?O)vvjXATn)3oNnf$v*Z2+wcZ)<4y+HiyN_ah~)`%epr2&h3EZfA*TqiF{ppWil|+13`nm&IK9&FsTzp OAfBhIpUXO@geCxrc`K^` diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-16_altform-unplated.png b/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-16_altform-unplated.png deleted file mode 100644 index 1f92251d9e5af4ed21e1d30bd781e201a1c0ce38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6x;OC_H@aoZ)oorOz%K1Lpr5tKB)uQ-#`1YniXEsB*N4`6{zsxgv9c zX2v7NGYTgazSr7a4|Wc?qUe9C;p>N~-^v%{INaplq{7>Ho!6t;HI%K}-{-Vu%sbW7 uH`1J6=3bkpbx%HoW7+E!|NpOvuVP;Dhs(;;+rI+nECx?kKbLh*2~7aRZA)YT diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png deleted file mode 100644 index 053eccc80ef28faaa5e37f88d009c28a2f18e87a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 253 zcmVqCJi;D&=x~FXeC^X(Uw>@HRsL;l4vyn& zI|)vk`agUPk&>?kwSQ}z3YOVMB$K{h?cA`S1o<=G+|N3yvp5r#00000NkvXXu0mjf Dl}2nV diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-256_altform-unplated.png b/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-256_altform-unplated.png deleted file mode 100644 index 31682a6a9a46447e6ceead530198d57e24e7ebba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1656 zcmbu9X;c$e6vro%1d>r=G%?B&nE@0K(YRrV0wDnsDMA)elp}(GhiEJ*BAaXjgrKD? zw#8Ge5JU|Xl|@CdAP`ZaMM+z$R6syc5v3?15Y`U#oSuH~{c_*Czx)5c`@Y;`B42?C zWCj5MV6s){Aprm!cEkZBJeCe#3fPV%p2%mrCzfaaUy%0xmWjDAq9wjkfU8iqc9pZJ zsWZWFak%%5Yq41f085<`ZaUcxVp=RxHJ1uqhWhUa zdGK+xuQ`2bis4Oyc-7^L@y^1j(21I$khjS(NxgB>w)>uzoaY}-Ty)(bU0Op)`b!w{ zawYhkgU>mBmbFsF&og6GvC8fPtXGsETB-4B^4*ha$R9rfRevCdF=O*G6|I-Z*MuS8 zqK|B;vo(KLv?BZoL2GYO^|9p?cY|`97pI8ZrnSxUHxRT8S5$glk=3MOEzTdKJDDCx z1T}`nJq0VV7R#ardkH)2T@Q`z4xP^4h#4vU;Ib<6kFZiDOoUYLT@pb9eTZy>q0*H7 zf3ccnu#uW2Q=D4Ke^9NMDYN=WqpqCJ*0?m75t|3Y<+&vyhv3Qa^y3BN($Swt+2SX> zWBIFif`eeKDC6WUaz*YkItWIFZ_D`Xm-5zWfzsL?@ka9_g@rHcVX8-%qj##RL{Ec1 z4Ub+8VcK%`AW@}l5|d18Sx~)qQywd|rOpKgUu(bI0=&da-A>&sa)w5QO$>9+MM}w| zx@G8oeh=kwu2$Su%P1vaVeRA-UZ!gsU=?CH{4NKQpU`GEs7X**Gx zm5p4-)OF#4)$MtkMIRTRYkjn*a4OpPqARoZ`jx{j9cXt~_kvkr#57*rbEEUdr`K+P zjNz9=>_|_>&RElY~KEJxPu&1lggxEGqDw48WNN9aVGx_pV+ z*swYHPGnV3cX@~(arcZ_^QM6^Zxg^krM@^9S7QGEDr-S_1oF3_Q|0oirk!oJEwV~#OGoR_+Jl}Mm5O{J0T(d=sfea?Q0_mm}>-mw2c~S?k&Zv1Fndn3y z5$I9YPQmINc9`9;K3SQ0SSRiyWk+}3=BOqqxLDY9`BwzrdZDbFdj>-RgWLoB9IgPH zpw1+o__%f9n%)ZjYf>~l4R3JtDFkwOk;BKZ*_g_1?)0K~;w-3{yO$l!enz!PEnzjG zk27mQ4)hRh!9Jd&y~VugKo1oHo;|OWpXPIWh}5=pZSJk~yUQn5Pelw;qO3Ae_;^8j z!h9rq`Mtw4WmPSwlv9EI34$z(DgQFw3$Go8UkG+^I8|rS*7sa;tyK47QJf}EWuy4h zu~qwSOX~n)->ZC*a0cFh#zNB5yD$6uz(qV)G!Zfo5)xgKApX(X2P;_dkS0bJPSa_q zz8L0UxQFn1TS!Ft4kvW0lzlDG;Z#HW&j-Nu7g(!t$z>~8dm(;yfg8F}!uF8u!gP6l z|JpKyAz6P&Y%92s<=G?Ha*91m7_#rBk;a_Ysw7Jj!CgB)xB+EuehpW@HolGadH0&V z+5b|qx~uZD!P5K9vr+Qvry5CrbLhQ&pI$~(&8%Jj-v4q_ Goc1>_qT81M diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-32_altform-unplated.png b/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-32_altform-unplated.png deleted file mode 100644 index 456a0a33d067f135f8d8b6ffb631f5da15da6e2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 314 zcmV-A0mc4_P)kdg00032Nkln1qYjh)xqjuI6wjtDggqAantQQu-xsv9RzgX@P}{f7d*H`}u|l3hRC* z2G-yTRi`gNF+rz8gL6y1i3n;-30Z%P9Kea?6)F~OM1w~;v+~VEMDW}BkE)!4@x*h` z7>oY&eE_-#KxdZi#=IvY!acLYq%6o9Qst#9Ug5Y3V{UBUG74AZr;-&Zl}}xfG{^zc zBBk=FD`JDSVREooIOA-X%sJy+kh3n|0m3)a?YWog$JhE79+uyWCu%d@%oxGoKL7v# M07*qoM6N<$f)3n-=l}o! diff --git a/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-48_altform-unplated.png b/TINK/TINK.UWP/Assets/Square44x44Logo.targetsize-48_altform-unplated.png deleted file mode 100644 index 378034e16fb8c5de9d8c1cdadcfdd4466630a300..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 422 zcmV;X0a^ZuP)zCxt+;rM*|LW*rYrT?( z;e&SuM$9mbA3^oFS8(9Mb%YSO2Y_Abo*)IuPTz?GIE?%N3wmJqG;H zwWn^ur{|hvOAQMvrKgT67<)krtq(aFl~uLs1fDksy_Fk~`(1kz2k6vM!@`6AA2R`4W~V z`L?`Fq{jHr>g`DhP!{2CAK5GGSY}++SFC7aR8Z{5osi7Y diff --git a/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-100.png b/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-100.png deleted file mode 100644 index 8081363087f43e8edd97c8122792a759d342af91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 921 zcmeAS@N?(olHy`uVBq!ia0y~yU^D}=r*SX?$z;xugFwnDz$e5NNdKn{aKv9~1DYaQ z666=ma9^Q8;C;e=hx!HcC-k2$$X{;|aAAwJ-VX)_rjMR3jv*CsZw@eW0+k;!_`Tfh zcw#h&4F(5pT2GGMo+!~ZX_M~BWVe6+*Oz@}Fn93qF1od7r%iNva$_SO`^n10l?k(% zE&pm=)v;~7oPX&++rGTjCL0Az9HmaS@OUeCHgsO{QHiLUmbODZ#Ld$2`EN&VX9b@$ z%cH#tJ5PIVI_=JV^6BDE?t_96e#T$)!_kKme&6KKR=nsl*-fxGG*PwX zEMMIJw8R_RQq&mMeVVrWqmA5>6xp_#8<|0u7QbYR=A6lYdC3~P&JDi50w$=bhT0_r zUk;g|#(MO$QjV&M*ww7-+YgFKY!f<^bYbsgfqTB<+dS>Xu4TEuUeB$rm?$v0Ql8Jy zN8Hcp*`uhp&W5qgl3sQ>&ug{9np6~HE=rx~s*zBU`DyS;<7f%5lG}!x=d3jjvZ*Ns zihMZ~9d7xvfYEbx&hxvzSNe7U&o?3xW5H@Dcn-CsG)VT#thKkL4v^33}(_X$`0 z)5B&8=X6e}a8+%88o1|~aIN;=fM3tnoKZ^jv+r*`Xr>ix%qIJSF=*+7g%wriUlnLknv{do#YJ=KO4!B(U{%(2G4X3Vq*h zF3VmTbHLr|2G6zw&vYhf%4Y~AofnlAy{F_jLFck<7PD3(m&1nBIp1`I-f2oi^*8%Y zo#4l7rod@gf2g1NV8K+eKWS@y?O23`BJGYeFYx#zsCGz+=MuxZnjdS8pQ|pJTlTYn zvy;W+m-?rNLPhExd3y4>LZ9kSwBO(Tdb`c#_(ShS$~hCA)aqZ>J&1q)A^+fy>Fsk2 yY?m^{eOh1kpn(mTgcB$H4Zi)j`6?qcK{FKe-M+s}c3W5~NWjz8&t;ucLK6TzM5%lL diff --git a/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-125.png b/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-125.png deleted file mode 100644 index 45736239bea3478886df77252ab18d2154165681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1149 zcmeAS@N?(olHy`uVBq!ia0y~yU~B=h_i!)+Nq<|-sX)ppz$e5NNdKn{aKv9~1DYaM z666=mAP`W{V6gxH{e*gj`R~^|kZTdZLS}mDiA|!xIn~nF{axSPG5Qw+-U)fYIZ0KI9Otx2Deb@fEI zbJy2xDs3yTN&P(1f5Gv@8_tujlBfTbDP-(STs4(RpYM^zp602G>m!-`o9}$y=bO7~ z_S5-~U!E1pFBdH@z4UW~ma&iTD}^13{0}q>aw7T7D)L%nSqeq`8kQdm37GnHyB+iV zG#}QARV$tete!Rhd~bzPi^aB8ny39eWc-hw3Vm|9XXOGdZtV}EKTH}|hOw^gxvQ4c z((rBiQ8lMqeY#uMYbI+Qzq}#YTldONnP0^=tlHPJ=Ra$+nUl$M{95y0lVwev0)jew z7PB`dulg9W=8j*D)J$o^d0fZ0{jC%T`@YQRsrr9?twssQjOs|G)jaIn2bM4VeZtLm z&y1$Hq@){rIFCaGnbsWS=s)z{?)~vQzd2YPcecf55{gMnhOQ(Nbp4PNn>E*^qwpk7S!k*`kf4DM3>(gxC{s{#(rpsO# z3QYVW;Xl*w!R03$ovV!ke$8)v{gtEhqsS`;J72!PzZDqH9$3C|VSRY&0R(LM04=NZTR%fJ0}>uen79b13r?fKxDyW;@QhPW@slkMW&ito{?j>5 eMD_%+cHEMG-NLhCiSK`15ZBYy&t;ucLK6VD3Pr5| diff --git a/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-150.png b/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-150.png deleted file mode 100644 index d10db3e247234d80de8c6cb8fa1b23f09668b994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1369 zcmbtUdsNZ~6b7Uu%?-^!nhJvU&_r7KK4uaKifI}i(>O`3fn^#Z8WukCl-V!^QCa2C zs7^jM&~$UZ3LVjWRT`bjd__)_hL#N%s33T8(R)Ccf{qz;%`r~C za)*uyFWcQOdPt~ku7rAlK+vWLQW%{No*tla?-1Z1t@zrOF9LKQ(0+~ds?DM!8;z6XIM(zhhD7Hwjufpo@Q35$bopA0Yz~f_cjd49i`Q;iixe_mh*_LXPND(6=PLi zBXS$I;^7t84A5!sk-}DN*)E6F0KJG6DL}Mv;042GyjZz3&aA$dtBd^|2yil(N~|@q z#*d^G1{2m=<2x`FOGN}a8Mr#s(v9Qc!97+7;Aw}+*x@11+vdh)>BaTkiRyg62NzP6 zm>K~)o8^xtYoAv#hBi3$kPF2OMyVWP|K0^&qW?n_iKlv~oCm)XF;{D8gtYQ|H>*45 zI-lv6c1=B4c@Qbm-$*<#8JA}AKJQIYkk1;=@C!To9ioaDoq$79Xmd+ImS-3qUFmY& zHCX5nRm{N0?>sTlCx@>2nrCh55|Cy+4)inn1hxL(w7Ey#F~+eQ`$Kc=uNf>9$yQzr zpSt0G&?<|kba>RM$vcdaXKm}qftbRvN#cErW8={x|6kV{R&{RdwO_pnBZaA{c;+S* zH`5JpPBc&WY54g=MS>k1E^u+=89N zc5nVKhqj`cV_v@Fn^W*>|E0R(F3K`4X|CCJzn@vCtzau=p*R3@>6C;fV>-t&XKrV3 zGpOdcSzCC>#&2;pj)})}FSBWcZSgBxdB(P&kXIKaIioCvDk|`@#uhH*^a;j$KZNI9 zVfqKvPB|}y&%Pnjbb97CbMTRzdU<23NhVQfb%zCylrNzWs;tg87yZO9nipDdI_ni( z^)8h1JpP1(7@-M2=QbLU=MSN#-vpmZ-!S9pvN$xMOzDF`aW)X@-u>wn0ePsl3~b(> zoy%@{7aX%H`O85y>V*vU<9G;?pto$um>$yUAA?_?4FH_r0>?xlJ?BN;hwhWnp zBZS{&aY(aJ&8^5wptwWcaUYL`E}VH=ZPLqC!cuZhe@`BX2DQ2wS(5H9MZi`Dz^)qf zNfpVZZ{C3md&EMhMt8P0V7B|35{Jznc%KPMLF#&sMR!PB(s+i>+Zza z+;v%?|?WnLW!RI}m6 z@~xKgrg`0{@8VD9*A12p7?_}P2f^yo#ttJEpc2Z3&^7Kz+(PhPA$<@9)V1I|)vYQ- z{eG_Y`Fu|6sO6flYSL5e&yjeCsNu5F`Y`Tb{@wjtF|$NI{XNq(pX?if$5YZm!9Tv2 z@6#s?Eg%~v-?G=uWlE(+Wq)E$*Ow|ei?`jy^~s;pf`*W(tKB+YSt-csgzzsQ&$!WS-vvrVyV?k+LV(e zb$UC=$XFIBMy7C-Y$&nKI=+^sYmBv>ul|94IQK92^?aWD`aGZOx$pavMGYj+wpnU} zKp!`_QhbM`EdER|q|v#l*SBeO z`)Xfw$jhZZS&s!2vxm1<1Ns`Y>QLr-wgfP~f=K4@3((=L-1crBj;1IAjDCh}V6g+3 zhaEnl+hWcpnv*KdL5{3eN01``JiO!HZ8K;Yu8TWoG-2}W8!@Hi*s62b;a4ua23uvU%ws0j*y8M{%9dm7Oi>uxk_QU4|YYN+u+ zV_BxziXuMX-GWjIfJY-N<8B!atOFSG+=HxrDwG0yI$3pLY18#!;Is~Gl~>H9KpI8>waUTFw_`JpWE#@*e9ra zwzJG15Bhv(SF@7Cy?@qNTKkPfL}k1hRYmM@M&6I^^V`(CEIr;3O1in>i$u?U-aBy5 zys*kySy1@KQ6RTH-#_6gq>fs7P5m^LPT&z$+{3Qz>_3@a1IfiE_)$x%fQ%I1m0+j_ zrhWn%s;#l8(xi&oGP{_;+^;Yp#B-}EJi*W**Ok=EW8|az;g1i9VRY3CSLXoDb7=I@ zxnqXYz%7WkvMQ@!F!jY0`vHztCQfL%26USU^;cYiY717g8qrxaJAeQn!9u^{GEmB%RJjMw#e9hGL9GPq2;zD! zP7b$I*Ldm=Sj{WQA#Mr1dce!U9h}w_14E-$OFR3>{1M`Z&x@?WQX<2KZ%}~p>lZch zm`&iuE4^XsPF*739>^S;DcsSAAVo!nrH0#{CVQN)1oW>O-bC&Q88dlFfiSCvoOYWc zR%e8FO{Ac3yhLqWdf@D3O`by?O4?xH^_6rKPVQ$BmeIJhT&IBigCn}ke zhiajubpBUxU1~4U)V#|Ka9p?UEU&*n`zT&^D6r)~=JAm`LgjoEZAbYaD=+BZ>ccIU zVGxgpG+<2c+IQ03)pOhq%vmL@P(B7%AF>`eArl;6v{XW$mlUR)J@(*e@e$OXC>;9E zvlWL@6X<-?JZ3gNZplM<{+)Ej3qwl;#O8T=BEiypZ4yG?*#{(VC-U7d_zB2_oQj|( zw7$XZ`#fcMWEX9is&t#ITT`MGYbEJ&s(Tdj3RYDiq8CVV;CTscxmi#->-~!rET;-l zy_?^9@3=AbYr8K3ljojNHtwI^%Z#j}#XeXkmXla?mJ08UiW}wtIyDt5(tqC^ZWk7T zl)xhVD@3>z3k&)@pW>2ZQYP}D&uJ+Uc44<6SPR^ZsHWoQy?V07_hDqziWJvbx9MUL zU5tATuixu}eD^AN4o-4V4AE)?<+DvF}XnccZlCv0&)(q1uCS#v)IcO1ssd zl`ZpyeZ?zo1cOu*E#gxQ+Kj8vw~H$7Zh33xYji&kID&GELiUshh%zjz(#X9EwaqNQ zZvXw|4g=F(-vAnq!Cr%mEp1@B@RT#(QVff^@C3*4h%#r^GMguwl^|!*TVSV%^qJF` zZ{K=^d$4kGCrp-SU($%~7x^C#&Q;3t?R)QEtCNYkIyXo-Z#p|T(a)(>E;*@Brf=J8 z7!m*VgVviQ*9}*5t#OYw}Fl66jyNIsDvzXQy?Y diff --git a/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-400.png b/TINK/TINK.UWP/Assets/Wide310x150Logo.scale-400.png deleted file mode 100644 index ad96c43c9fab08bd597452d3ba5e084b4e5b11e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3682 zcmc&%X;@N;8a_xxi^*h*$~v06s|R36 z;pKOJnR{J@wH18F2@T^OV&8qU3z+l*b(A{nxATmI4R^#}=`DN2=?>TSDJ`5%e8rga z!c(oV1uZ9enANGm*-D-D8du$(#$;=Nu>a`jiuy<8e=io%tnU2jw|j?RTWyWSEQZr| zF7Ni{&?l4IfEUw#PY)9^PXm!T^#0&0bBK}cE#F^PP9iyjewW<9xK$p#e`m~|u52yI zdZJ!|-%yyVlbRw5&x6}+8;yY7x5;qQ)EJ4WkH&SSUwcknlP0mriMk?Kx`}Qc@ECT< zW*z&ghDX*uek$O+A0bB(HL>ryT}B@Rp>%TzLR2veJOMILvDOGf1&;~1BgF_xc)`Qt z^H*K#aL}!x3e0$II_k@jny_{(C*h6JoThzaFcSVHlH(LS|M(h}({$Y9#N~6a&Z3Kb z@B#-<=s8Zgj?+}52C&u;HuqNpjW9~mHyenlr%Ru5JY{pP>p#Uq8%Yi zasj;c3z(k{azQI#|ME z6j}$5Oj5CA{!52f4v-qTvHKkE5qs-L_`@^(TU0=neXOZ6yJ@UtjwMD+lExd?rSz{ywNZpM`CbZAKX05G)i21eJ=LmZ!-96G68jixPHJ; zQI}7XxA{S%ZC?8wR|~QYcxiB3qQ+hLT?GqsZI2kcoxmvf`K;m24cXkGy+x+5kUi9~ zb?|plHn{Q4R6uqovZ1G3kvj?$ss;OcnXu#WHfiISUsI^I8EcAvBH~(F<0f@Qo+*n3peNI zlJoMJAq)EU6j%OKZntb84(_{G$p0$4p=*XNP2x%eO&vkEQ=ZZnMa1yWZr?+~yKg=D zA~2!7OIMWQTI2#2V8fD`9I@TltJmtaV#q@|Rb{*9&Y7wJogAZiEpW{EYVpa(^Mz0u1YBK6RR;++* z;jHCu)?vLc!SEK$lP9HvTCnn$Q%aW`I*(0qNb{qLV2w=;{s+o#?93GG_DN``+|fvM zz}t8W%4b)^dEN&CdX8M^-Fd_>p!Ki4`t2@>bAgEIp>M0e@DWX<3{H;ZSH9d3oJT%r zHBUJu(6Ftq(p333Y^FeLH z7SxF!?S6^~epCIN>R(uzHwo-(u+Vv&tO#B6>w%-5MRv&K5FN8by+tGCWzm9k|2?0o zm!PPZPCul{E2Ijtd=>8SjU zy(&z$N8uglyvE4w&7QncyEsMA9!fc5pO$| z@J5oCM?>R39@j8PV&;(X&0b}Xo8ZRi=!&@E&W{u4Ny!Y3$}Vc6=0V#ltt@vWkEwsh zrD1D@fqOoz{P!$5?gC2~oxx^l<HY@u+xLjvwPuZFC}*>}n; zQ9xYGasX)`ww--Hdm@0noe$R|5ysluJ8cfcf9b{`MCsh#7Zte3>ZzbC#AMJejAGy{ zYi!Trkz<_|g{3QH%f74?q6}4$)dDUnm%J5L{;^^jcY!QTf})cvvg`w;N!wu5?&c`| z(-`<@Y7;z8`R*K_UqJ8~f+ccu34Vc9gb%DPLscCaUzSfJ!rwa&R@8SqhxpRa>eX8a z0rHY^5~T6<+4oaN1kB|Wrxuw9_freT2^v_ZBxC{X>e=__A3?b5YElOWEsx&LB@-4E zls2-59>%a9K|@pphw$5GQT<91;5TJcpeQ1!R&C&9RqlQEG5KeO*c~|{9w@}s4OBGZ z4{A|vJf%j+&HS?vB!VQhsMG;!iV`z|+Tyw_e>4N^k3_r0$oMi+wrI$#psKx%`xoXibV*8A za-A0$-~ILpvK(~GCq?ZR8KUN9+;YirzM1xs*W`GOBE`d~+9JJ)v9{|CT zSqMWFj{!vcYLb`Pj(x8HjL}ag$Uq`82|jz8(+<_csGLk4eIFW>gQZ;;eyJYas~->0ist<5C#_ zVFMj)^^GG9O~Qk##^k{?CB~4V1qSmtOKac``2K?O5IEbhqu~H`7?jN1VG|d1#hlZ( zZwN*rIBRkEP3de-U#~V>D=a990j#TW0`V?Qi(ul~Q)<9AfdNPm5GDjbP5cWEMEc4= zPhrrO@|f+Q>NI_jD+_^b#H7pMh;K?>A~R5;U2VoU7md_Dkea~IHR#@24Ocqp{$weq z-w#0x=t00!JPcl)SSE_beQ5z6H_Y55!VNRsFP*#7Lj(v{dUdfk2i>MEtG11<5RLo9 zMd!fUQNr{8B7E#&Ym}g_rLrZh38lBLT{2CC{rw-u|34y{gGpg7ofr&r1tiAdfb0Ie Iy_BE+3;tq=qW}N^ diff --git a/TINK/TINK.UWP/Assets/tink2.png b/TINK/TINK.UWP/Assets/tink2.png deleted file mode 100644 index 6b76f579649f83b6057d9685f84ac27c55d420c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3765 zcmai1c{tSV*ZHVmH7If;)_!~I{0>C-WzYWwSv8@9D>~6;T*KI;4*Wc=t`Kt58k_^>|y~j-Z zqH(#mnRPyKUXdw+dP96<7%xm=q**xM`}1FKN@H^Dv0)Zrm5ho}z{AD`NHl+iMcEW0 zAti0?-bc$h)sPL$_v4Mou;jGU)6(AF-h!M7r>-BT2Zvt2$Mw7P)%OI-!K1QtX9M?}qMslZgf$0z52+Bci^ zz1EAz^e`YV`$tl~0v!|pK>;g(2`dSl_I=9aGz38zus|Fb&V&U{N&N>v59bDSfm5cZ z@BaW?#Hahw!86mO$RjeD`O@*a&`AVsd3S%)uS*_JqpDrxfTw1GSEh6#_E}%X6x6ST z>|hROY=J@k@QPP9q6lf*IHI@C$HU)pq%c0Q<4BJ)BqZB#(!!2xPT6A=Rx{Qk0{$o zwJ7x7R<$^7DyM1@b(k3bOk;luKz94vStz}=C+kpW1LbUFD&L>UE-NT8QxYdelx_{U z8KNr(O)BoDgs<(Qq(V(Le59~DFrt|f2bW)MSVIPj#mQ`{xWwv6puw_h@9T2$k8fkD z+TyPzcS=GmpHmv>;icRJ_UzzWl?TbH=$Z7p(sw+l-!H&yr-?VJFTqzmtP?CK+c|=G z+W>silIaFO>H*SFrgB)vADR5HI(a$sY z+*C$i3q8jNLgHM_XH%uf3$3VCL9H{x_-4^FDbn20#EV7>5M;uU<=F*l~greG)+xa z5ni(LtkG}hGViXhwUwCRvXCWR@i+2^U@qe5 zrofqZa#G4qk{ZB>zSM0%atu+pVTedm$$k&i@~mTqul#&ddxftoY=?WR=Bd?YIT+DZ zK1iq`Mu%MW=t|Af;O)zwA6FL1w`)eGVmc9+mDkzl4L9vv5qpJq|2#VJKO>>m5+Hr>&L2T3$0O zB4*sA0e31wbp|#_ICRRZTl-QSW5L6B_un^<^9{T9x%*Aq)4f`6^7>&fP9Wh63~T#x zk`F#+OW6mU)PI(&z?4+)rgv(y!jf(|A+N_O7WVU3_h?2`D+3dZ#b5K!w<0D}IxmJR zaT@zZ)k&0eB!vVORP!++x<6skz12iV*HdfT>K2TyuW)+2>nh$a>9Tf|x1}&7^+ncx z9@u@hS(cxYt>MzU&8!X%?sfXaPFGz2HESg3!u_s7U93^Lnk63?@$x`LrD~zDhikk#nI65I_@4*6Sjk97baG=&<@5=scw&UKG6p5rkRz2ZZ0Y90WweHqGf<<5UiFsYe;_L{o)SJY zjCp;o)ja&=lk&lrWwuKA({4uWExwR3=E3+t?Wk;5CUVJUb!#iL`PKZ4h_aU<=|1L~ zWdVYkJd%Yl0P*a}q}S+3FpC7G3N_ zDS~s-nKeXcx5}Kti?$Hdvq@4ZK{?fgDOPCR7sW2IUH^();@cvKruCk-OcrayvljXg zssjb%B)!xj|IE;P)?pb{HlY$s8v?(wZ~N)a)ha!BdxvGWIQlRl>8Zb#C@!J9Xcj+L z&fU28bx(z|bL4zI&#e)ul`^}pLK&upjwDdXP`62bE1854mXhz=J%0Ync4^LO3e&Os z=Vce=_E}?pns8`|!v%fkrVG5NPlvX&9%WyfI`}~x7V+L4AM~eWuKDDLst@(D;B#2| zcXa8m$Ax#*{XRsURMIF%M6j;gYbM{fanp*03{m{K70*Q4YA7v46}OyVkwzAH!z)J5 zqS5|V2Ax0$si+fQ2^XEs_^g*%d^r<&w$DI825}GSTdNoc0ycMm6%%O1)i@%vzUh}C z7MNcKmG6G@{~WO3p5L(Clm#K&FWO_On`a#|Zmorp?Rq4{swfKVPX~Z7Lm=M$Oooyu z@7jT(zCH?!x2>5ePuFtSXLYQ~>sY;SUQYm+AKbvLVC}f(7335L+0}8zZ*7y%J&u zdei-}>2n)Q7$Xj ztiqWAd$7CLnvxQAF<~Fs2s=R{;wzkg#Qsm&5hi;gmVV(93o@7t{!o~ zB?$pa=T`X18aJiv9ml^|RR?51a=RWU&hH{hj$M87UQO(4_0CMC*&TxW3(|Ug1=G4} z2uK5ZSP(y4Dm2}wh8@1*jYV#H->)2aZR9`kebB9B3u&}=7CY=0>#FRWB$er?m!^UH zP(_-jy|+6btE{JTF6vK?Bejr(OiX)aDhib7v~K-yoX4<__4(|<(=7}giB9vVr|Q@%?!H4{o8>ToIqA%3 zDxqRg@b;dnNg|;lvh%#;y}6RDFO=U>+tu_ir!CiC*1%U{;gJQ@W_cS_Ll==n9C>4; z1$15H!d(hTo3Wtx0~K`TdTM^zOBM%~{(7m7T{}P#l&vz!Sw?jhV6XZ%9`zBrwuSi6 zssVoNmp_CpHIX!H*$*FNuy>7uaH9g-j2-CT)7x~&OfsG1edJfcy2e-&w&=h+y2=0p z!duQ=7p>po3@N1Kq3t7@KK+HDe=DMcMV?mbC@sX*G zJKVHEJeAZ`;04rd!w-GJNhW0XpKVr|B#7j>(iDZI0B3?twanw+PENqrz^me1$&XAA zr=vYP?l!Add*qsGCN)D4D-Utr&i&lJ`uDXX)m!bADN$h`~Aacl4PIWT|4(B^;uGiSnFxYj&Q;T)ET~83#w-&f}SpRO4uQycYiXb+F&n l_5QE#27>y^kg|UubZ3SAH9wk9pYEZ6v4MqtrS9#h{{^F?*y{iQ diff --git a/TINK/TINK.UWP/Device/WinPhoneDevice .cs b/TINK/TINK.UWP/Device/WinPhoneDevice .cs deleted file mode 100644 index e27ae99..0000000 --- a/TINK/TINK.UWP/Device/WinPhoneDevice .cs +++ /dev/null @@ -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 - { - /// Gets unitque device identifier. - /// Gets the identifies specifying device. - 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); - } - - /// Close the application. - public void CloseApplication() - { - Application.Current.Exit(); - } - } -} diff --git a/TINK/TINK.UWP/MainPage.xaml b/TINK/TINK.UWP/MainPage.xaml deleted file mode 100644 index 52d37bf..0000000 --- a/TINK/TINK.UWP/MainPage.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/TINK/TINK.UWP/MainPage.xaml.cs b/TINK/TINK.UWP/MainPage.xaml.cs deleted file mode 100644 index 95e1b70..0000000 --- a/TINK/TINK.UWP/MainPage.xaml.cs +++ /dev/null @@ -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()); - } - } -} diff --git a/TINK/TINK.UWP/Package.appxmanifest b/TINK/TINK.UWP/Package.appxmanifest deleted file mode 100644 index 3dcc876..0000000 --- a/TINK/TINK.UWP/Package.appxmanifest +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - TINK.UWP - Oliver - Assets\StoreLogo.png - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TINK/TINK.UWP/Properties/AssemblyInfo.cs b/TINK/TINK.UWP/Properties/AssemblyInfo.cs deleted file mode 100644 index 62660e6..0000000 --- a/TINK/TINK.UWP/Properties/AssemblyInfo.cs +++ /dev/null @@ -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)] \ No newline at end of file diff --git a/TINK/TINK.UWP/Properties/Default.rd.xml b/TINK/TINK.UWP/Properties/Default.rd.xml deleted file mode 100644 index 80a960c..0000000 --- a/TINK/TINK.UWP/Properties/Default.rd.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/TINK/TINK.UWP/TINK.UWP.csproj b/TINK/TINK.UWP/TINK.UWP.csproj deleted file mode 100644 index 06b3f94..0000000 --- a/TINK/TINK.UWP/TINK.UWP.csproj +++ /dev/null @@ -1,171 +0,0 @@ - - - - - Debug - x86 - {B3DC74E1-DA60-4844-BB25-A7F6D2BB6A9D} - AppContainerExe - Properties - TINK.UWP - TINK.UWP - en-US - UAP - 10.0.15063.0 - 10.0.10586.0 - 14 - true - 512 - {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - TINK.UWP_TemporaryKey.pfx - 3.0 - - - true - bin\ARM\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - ARM - false - prompt - true - - - bin\ARM\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - ARM - false - prompt - true - true - - - true - bin\x64\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - x64 - false - prompt - true - - - bin\x64\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - x64 - false - prompt - true - true - - - true - bin\x86\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - x86 - false - prompt - true - - - bin\x86\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - x86 - false - prompt - true - true - - - - - - - - App.xaml - - - - MainPage.xaml - - - - - - Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - - - {b77f4222-0860-4494-a07c-ee8e09fa9983} - TINKLib - - - - 14.0 - - - - - \ No newline at end of file diff --git a/TINK/TINK.UWP/project.json b/TINK/TINK.UWP/project.json deleted file mode 100644 index 142e3a5..0000000 --- a/TINK/TINK.UWP/project.json +++ /dev/null @@ -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": {} - } -} \ No newline at end of file diff --git a/TINK/TINK.iOS/Device/Device.cs b/TINK/TINK.iOS/Device/Device.cs index c0387c0..ac2185f 100644 --- a/TINK/TINK.iOS/Device/Device.cs +++ b/TINK/TINK.iOS/Device/Device.cs @@ -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; /// Gets unitque device identifier. /// Gets the identifies specifying device. - public string GetIdentifier() - { - return UIKit.UIDevice.CurrentDevice?.IdentifierForVendor?.AsString() ?? string.Empty; - } + public string Identifier + => UIKit.UIDevice.CurrentDevice?.IdentifierForVendor?.AsString() ?? string.Empty; } } \ No newline at end of file diff --git a/TINK/TINK.iOS/Entitlements.plist b/TINK/TINK.iOS/Entitlements.plist index e9a3005..29a7233 100644 --- a/TINK/TINK.iOS/Entitlements.plist +++ b/TINK/TINK.iOS/Entitlements.plist @@ -1,7 +1,10 @@ - + + keychain-access-groups + + $(AppIdentifierPrefix)com.TeilRad.sharee.bike + - diff --git a/TINK/TINK.iOS/Info.plist b/TINK/TINK.iOS/Info.plist index c1faf0d..73207e9 100644 --- a/TINK/TINK.iOS/Info.plist +++ b/TINK/TINK.iOS/Info.plist @@ -49,8 +49,8 @@ CFBundleDisplayName sharee.bike CFBundleVersion - 222 + 238 CFBundleShortVersionString - 3.0.222 + 3.0.238 diff --git a/TINK/TINK.iOS/Sharee.iOS.csproj b/TINK/TINK.iOS/TINK.iOS.csproj similarity index 99% rename from TINK/TINK.iOS/Sharee.iOS.csproj rename to TINK/TINK.iOS/TINK.iOS.csproj index 19afad7..3cc3dc0 100644 --- a/TINK/TINK.iOS/Sharee.iOS.csproj +++ b/TINK/TINK.iOS/TINK.iOS.csproj @@ -47,7 +47,7 @@ full false bin\iPhone\Debug - __IOS__;__MOBILE__;__UNIFIED__;DEBUG;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT + DEBUG;USEFLYOUT prompt 4 false @@ -61,7 +61,7 @@ none true bin\iPhone\Release - __IOS__;__MOBILE__;__UNIFIED__;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT + USEFLYOUT prompt 4 ARMv7, ARM64 @@ -78,7 +78,7 @@ none True bin\iPhone\Ad-Hoc - __IOS__;__MOBILE__;__UNIFIED__;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT + USEFLYOUT prompt 4 False @@ -94,7 +94,7 @@ none True bin\iPhone\AppStore - __IOS__;__MOBILE__;__UNIFIED__;SHOWAGBV1;USEBROWSERFORUSERMANAGEMENT + USEFLYOUT prompt 4 False @@ -109,16 +109,16 @@ - + - 1.3.0 + 1.5.2 - 1.3.0 + 1.5.2 - + diff --git a/TINK/TINK/App.xaml.cs b/TINK/TINK/App.xaml.cs index 8d74b22..9a5c35c 100644 --- a/TINK/TINK/App.xaml.cs +++ b/TINK/TINK/App.xaml.cs @@ -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(); + 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 settingsJSON = new Dictionary(); 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(); // 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(), + (isConnected, activeUri, sessionCookie, mail, expiresAfter) => ConnectorFactory.Create(isConnected, activeUri, $"sharee.bike/{appInfoService.Version}", sessionCookie, mail, expiresAfter), + GeolocationServicesContainer, null, /* locksService */ - DependencyService.Get(), + DependencyService.Get(), specialFolders, new Cipher(), - CrossPermissions.Current, + null, // Permissions, no more used. #if ARENDI DependencyService.Get(), #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 } /// Concatenates all log files to a single one. - /// File name of attachment. + /// Full file name of attachment. 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 { $"{{\"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; } /// Deletes an attachment if there is one. - private static void DeleteAttachment() + /// Folder to delete, is null folder is queried from model. + 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); - } - - - /// TINK app starts up. - 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; } + + /// + /// Service to manage permissions (location) of the app. + /// + public static Plugin.Permissions.Abstractions.IPermissions PermissionsService => CrossPermissions.Current; + + /// + /// Service to manage bluetooth stack. + /// + public static Plugin.BLE.Abstractions.Contracts.IBluetoothLE BluetoothService => Plugin.BLE.CrossBluetoothLE.Current; + + /// + /// Service container to manage geolocation services. + /// + public static IServicesContainer GeolocationServicesContainer { get; } + = new Services.ServicesContainerMutable( + new HashSet { + new LastKnownGeolocationService(DependencyService.Get()), + new SimulatedGeolocationService(DependencyService.Get()), + new GeolocationService(DependencyService.Get()) }, + typeof(LastKnownGeolocationService).FullName); } } diff --git a/TINK/TINK/BackdoorMethodHelpers.cs b/TINK/TINK/BackdoorMethodHelpers.cs index 3425ac8..1ab4636 100644 --- a/TINK/TINK/BackdoorMethodHelpers.cs +++ b/TINK/TINK/BackdoorMethodHelpers.cs @@ -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); } /// Gets the current page assumed that app is master detail page. diff --git a/TINK/TINK/Model/User/Account/Store.cs b/TINK/TINK/Model/User/Account/Store.cs index 960b430..9c63773 100644 --- a/TINK/TINK/Model/User/Account/Store.cs +++ b/TINK/TINK/Model/User/Account/Store.cs @@ -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 { /// /// Holds the name of the application. @@ -21,12 +23,9 @@ namespace TINK.Model.User.Account /// Holds the id of the session. 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. /// /// - public IAccount Load() + public async Task 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(), - account.Properties.ContainsKey(KEY_DEBUGLEVEL) && !string.IsNullOrEmpty(account.Properties[KEY_DEBUGLEVEL]) ? Permissions.Parse(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>(xamAccountStore.Properties[KEY_GROUP]) : new string[0], + xamAccountStore.Properties.ContainsKey(KEY_DEBUGLEVEL) && !string.IsNullOrEmpty(xamAccountStore.Properties[KEY_DEBUGLEVEL]) ? Permissions.Parse(xamAccountStore.Properties[KEY_DEBUGLEVEL]) : Permissions.None); #else return new Account( string.Empty, @@ -70,22 +69,22 @@ namespace TINK.Model.User.Account /// /// Writes mail address and password to account store. /// - /// - public void Save(IAccount p_oAccount) + /// + 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 /// /// Deletes mail address and password from account store. /// - 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(); - } } } diff --git a/TINK/TINK/TINK.projitems b/TINK/TINK/TINK.projitems index f604d5d..3862ac9 100644 --- a/TINK/TINK/TINK.projitems +++ b/TINK/TINK/TINK.projitems @@ -14,7 +14,6 @@ - @@ -76,6 +75,7 @@ WhatsNewPage.xaml + Code diff --git a/TINK/TINK/Sharee.shproj b/TINK/TINK/TINK.shproj similarity index 100% rename from TINK/TINK/Sharee.shproj rename to TINK/TINK/TINK.shproj diff --git a/TINK/TINK/View/Account/AccountPage.xaml.cs b/TINK/TINK/View/Account/AccountPage.xaml.cs index b66866f..a3d5c6c 100644 --- a/TINK/TINK/View/Account/AccountPage.xaml.cs +++ b/TINK/TINK/View/Account/AccountPage.xaml.cs @@ -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 { /// Refernce to view model. AccountPageViewModel m_oViewModel = null; @@ -31,18 +37,18 @@ namespace TINK.View.Account } /// Displays altert message. - /// Title of message. - /// Message to display. - /// Type of buttons. - 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); + /// Title of message. + /// Message to display. + /// Type of buttons. + public new async Task DisplayAlert(string title, string message, string cancel) + => await App.Current.MainPage.DisplayAlert(title, message, cancel); /// Displays altert message. /// Title of message. /// Message to display. /// Detailed error description. /// Type of buttons. - 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); /// Displays alert message. - /// Title of message. - /// Message to display. - /// Text of accept button. - /// Text of button. + /// Title of message. + /// Message to display. + /// Text of accept button. + /// Text of button. /// True if user pressed accept. - public new async Task 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 DisplayAlert(string title, string message, string accept, string cancel) + => await App.Current.MainPage.DisplayAlert(title, message, accept, cancel); + + /// Displays detailed alert message. + /// Title of message. + /// Message to display. + /// Detailed error description. + /// Text of accept button. + /// Text of cancel button. + /// True if user pressed accept. + public async Task 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); /// /// Displays an action sheet. /// - /// Title of message. - /// Message to display. - /// Text of button. + /// Title of message. + /// Message to display. + /// Text of button. /// /// Buttons holding options to select. /// Text selected - public new async Task 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 DisplayActionSheet(String title, String cancel, String destruction, params String[] p_oButtons) + => await base.DisplayActionSheet(title, cancel, destruction, p_oButtons); +#if USEMASTERDETAIL || USEFLYOUT /// /// Creates and a page an shows it. /// /// Type of page to show. - 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 + /// Shows a page. + /// Route of the page to show. + public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route); +#endif /// Pushes a page onto the modal stack. /// Page to display. @@ -86,6 +108,7 @@ namespace TINK.View.Account public Task PopModalAsync() => throw new NotSupportedException(); +#if USEMASTERDETAIL || USEFLYOUT /// Delegate to perform navigation. private INavigationMasterDetail m_oNavigation; @@ -98,6 +121,7 @@ namespace TINK.View.Account set { m_oNavigation = value; } } +#endif /// /// Invoked when page is shown. /// Starts update process. @@ -122,7 +146,10 @@ namespace TINK.View.Account /// Page to display. public async Task PushAsync(ViewTypes p_oTypeOfPage) => await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType())); - +#if USCSHARP9 public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); +#else + public Task DisplayUserFeedbackPopup() => throw new NotSupportedException(); +#endif } } \ No newline at end of file diff --git a/TINK/TINK/View/Bike/ILockItBike.xaml b/TINK/TINK/View/Bike/ILockItBike.xaml index b6f81a6..40c5cbf 100644 --- a/TINK/TINK/View/Bike/ILockItBike.xaml +++ b/TINK/TINK/View/Bike/ILockItBike.xaml @@ -15,7 +15,14 @@ Padding="10"> /// Text to extract positon from. /// Position object. - public static Station.Position GetPosition(string p_strGps) + public static Station.Position GetPosition(GpsInfo gps) { - if (p_strGps == null) + if (gps == null) { return null; } - var l_oPosition = p_strGps.Split(','); - - if (l_oPosition.Length != 2) - return null; - double l_oLatitude; - if (!double.TryParse(l_oPosition[0], NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLatitude)) + if (!double.TryParse(gps.latitude, NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLatitude)) return null; double l_oLongitude; - if (!double.TryParse(l_oPosition[1], NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLongitude)) + if (!double.TryParse(gps.longitude, NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLongitude)) return null; return new Station.Position(l_oLatitude, l_oLongitude); @@ -354,5 +351,15 @@ namespace TINK.Model.Connector ? new Uri($"{bikeInfo.uri_operator}/{CopriServerUriList.REST_RESOURCE_ROOT}") : null; } + + /// Gets the copriversion from. + /// Response to get version info from. + /// COPRI version + public static Version GetCopriVersion(this CopriVersion response) + => response!= null + && !string.IsNullOrEmpty(response.copri_version) + && Version.TryParse(response.copri_version, out Version copriVersion) + ? copriVersion + : throw new InvalidResponseException($"Can not get version info from copri response {response?.copri_version}."); } } diff --git a/TINKLib/Model/Connector/Updater/UpdaterJSON.cs b/TINKLib/Model/Connector/Updater/UpdaterJSON.cs index 3e4fd21..fbcca1e 100644 --- a/TINKLib/Model/Connector/Updater/UpdaterJSON.cs +++ b/TINKLib/Model/Connector/Updater/UpdaterJSON.cs @@ -1,11 +1,11 @@ using System; using TINK.Model.Bike; using TINK.Model.Station; -using TINK.Model.Repository.Response; +using TINK.Repository.Response; using TINK.Model.User.Account; using System.Collections.Generic; using TINK.Model.State; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using Serilog; using BikeInfo = TINK.Model.Bike.BC.BikeInfo; @@ -190,8 +190,8 @@ namespace TINK.Model.Connector string p_strMail, Func p_oDateTimeProvider) { - var l_oBikesDictionary = new Dictionary(); - var l_oDuplicates = new Dictionary(); + var l_oBikesDictionary = new Dictionary(); + var l_oDuplicates = new Dictionary(); // Get bikes from Copri/ file/ memory, .... if (p_oBikesAvailableResponse != null @@ -295,7 +295,7 @@ namespace TINK.Model.Connector return null; } - if (bikeInfo.station == null) + if (string.IsNullOrEmpty(bikeInfo.station)) { // Bike available must always have a station id because bikes can only be returned at a station. Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. No station info set."); @@ -485,7 +485,11 @@ namespace TINK.Model.Connector return new Bikes.Bike.TariffDescription { Name = tariffDesciption?.name, +#if USCSHARP9 Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null, +#else + Number = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?) null, +#endif FreeTimePerSession = double.TryParse(tariffDesciption?.free_hours, NumberStyles.Any, CultureInfo.InvariantCulture, out double freeHours) ? TimeSpan.FromHours(freeHours) : TimeSpan.Zero, FeeEuroPerHour = double.TryParse(tariffDesciption?.eur_per_hour, NumberStyles.Any, CultureInfo.InvariantCulture, out double euroPerHour) ? euroPerHour : double.NaN, AboEuroPerMonth = double.TryParse(tariffDesciption?.abo_eur_per_month, NumberStyles.Any, CultureInfo.InvariantCulture, out double aboEuroPerMonth) ? aboEuroPerMonth : double.NaN, diff --git a/TINKLib/Model/Device/ISmartDevice.cs b/TINKLib/Model/Device/ISmartDevice.cs new file mode 100644 index 0000000..fe000e1 --- /dev/null +++ b/TINKLib/Model/Device/ISmartDevice.cs @@ -0,0 +1,21 @@ +namespace TINK.Model.Device +{ + public interface ISmartDevice + { + /// Gets unitque device identifier. + /// Gets the identifies specifying device. + string Identifier { get; } + + /// Manufacturer (Samsung). + string Manufacturer { get; } + + /// Device Model (SMG-950U, iPhone10,6). + string Model { get; } + + /// Platform (Android). + string PlatformText { get; } + + /// Operating System Version Number (7.0) as text + string VersionText { get; } + } +} diff --git a/TINKLib/Model/ITinkApp.cs b/TINKLib/Model/ITinkApp.cs index 67ba1dc..a4866ae 100644 --- a/TINKLib/Model/ITinkApp.cs +++ b/TINKLib/Model/ITinkApp.cs @@ -1,16 +1,16 @@ -using Plugin.Permissions.Abstractions; -using Serilog.Events; +using Serilog.Events; using System; using System.Threading; using TINK.Model.Connector; using TINK.Model.Device; using TINK.Services.BluetoothLock; using TINK.Model.Services.CopriApi.ServerUris; -using TINK.Model.Services.Geolocation; using TINK.Settings; using TINK.ViewModel.Map; using TINK.ViewModel.Settings; using TINK.Services; +using TINK.Model.Station; +using System.Collections.Generic; namespace TINK.Model { @@ -35,7 +35,7 @@ namespace TINK.Model IFilteredConnector GetConnector(bool isConnected); /// Name of the station which is selected. - int? SelectedStation { get; set; } + IStation SelectedStation { get; set; } /// Polling periode. PollingParameters Polling { get; set; } @@ -67,6 +67,10 @@ namespace TINK.Model /// Gets the minimum logging level. LogEventLevel MinimumLogEventLevel { get; set; } + /// Gets a value indicating whether reporting level is verbose or not. + bool IsReportLevelVerbose { get; set; } + + /// Updates logging level. /// New level to set. void UpdateLoggingLevel(LogEventLevel p_oNewLevel); @@ -77,22 +81,19 @@ namespace TINK.Model /// Holds the different lock service implementations. LocksServicesContainerMutable LocksServices { get; } - /// Holds the different geo location service implementations. - ServicesContainerMutable GeolocationServices { get; } - /// Holds available app themes. ServicesContainerMutable Themes { get; } /// Reference of object which provides device information. - IDevice Device { get; } - - /// Os permission. - IPermissions Permissions { get; } + ISmartDevice SmartDevice { get; } /// Holds the folder where settings files are stored. string SettingsFileFolder { get; } /// Holds the external path. string ExternalFolder { get; } + + /// Holds the stations availalbe. + IEnumerable Stations {get; set;} } } diff --git a/TINKLib/Model/Logging/LogEntryClassifyHelper.cs b/TINKLib/Model/Logging/LogEntryClassifyHelper.cs index ed8ae23..1177689 100644 --- a/TINKLib/Model/Logging/LogEntryClassifyHelper.cs +++ b/TINKLib/Model/Logging/LogEntryClassifyHelper.cs @@ -1,6 +1,6 @@ using Serilog; using System; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; namespace TINK.Model.Logging { diff --git a/TINKLib/Model/Logging/LoggerConfigurationHelper.cs b/TINKLib/Model/Logging/LoggerConfigurationHelper.cs index 49a6c66..d9a2fe5 100644 --- a/TINKLib/Model/Logging/LoggerConfigurationHelper.cs +++ b/TINKLib/Model/Logging/LoggerConfigurationHelper.cs @@ -18,61 +18,61 @@ namespace TINK.Model.Logging public static class LoggerConfigurationHelper { /// Holds the log file name. - private static ILoggingDirectoryManager m_oDirectoryManager = new EmptyDirectoryLoggingManger(); + private static ILoggingDirectoryManager DirectoryManager { get; set; } = new EmptyDirectoryLoggingManger(); /// Sets up logging to file. - /// Object to set up logging with. + /// Object to set up logging with. /// Object to get file informaton from. - /// Specifies rolling type. - /// Count of file being retained. + /// Specifies rolling type. + /// Count of file being retained. /// Logger object. public static LoggerConfiguration File( - this LoggerSinkConfiguration p_oLoggerConfiguration, - string p_strLogFileFolder, - RollingInterval p_oRollingInterval = RollingInterval.Session, - int p_iRetainedFilesCountLimit = 10) + this LoggerSinkConfiguration loggerConfiguration, + string logFileFolder, + RollingInterval rollingInterval = RollingInterval.Session, + int retainedFilesCountLimit = 10) { - if (m_oDirectoryManager is EmptyDirectoryLoggingManger) + if (DirectoryManager is EmptyDirectoryLoggingManger) { // Roll file only once per app session. try { - m_oDirectoryManager = new LoggingDirectoryManager( + DirectoryManager = new LoggingDirectoryManager( Directory.GetFiles, Directory.Exists, (path) => Directory.CreateDirectory(path), System.IO.File.Delete, - p_strLogFileFolder, + logFileFolder, Path.DirectorySeparatorChar, - p_iRetainedFilesCountLimit); + retainedFilesCountLimit); } catch (Exception l_oException) { Log.Error("Log directory manager could not be instanciated successfully. {@l_oException}", l_oException); - m_oDirectoryManager = new EmptyDirectoryLoggingManger(); + DirectoryManager = new EmptyDirectoryLoggingManger(); } } try { - m_oDirectoryManager.DeleteObsoleteLogs(); + DirectoryManager.DeleteObsoleteLogs(); } catch (Exception l_oException) { Log.Error("Not all obsolte log files could be deleted successfully. {@l_oException}", l_oException); } - if (p_oLoggerConfiguration == null) + if (loggerConfiguration == null) { return null; } - return p_oLoggerConfiguration.File( + return loggerConfiguration.File( new JsonFormatter(), - m_oDirectoryManager.LogFileName, + DirectoryManager.LogFileName, /*shared: true, // Leads to exception if activated.*/ rollingInterval: Serilog.RollingInterval.Infinite, - retainedFileCountLimit: p_iRetainedFilesCountLimit); + retainedFileCountLimit: retainedFilesCountLimit); } /// Gets all log files in logging directory. @@ -82,7 +82,7 @@ namespace TINK.Model.Logging { try { - return m_oDirectoryManager.GetLogFiles(); + return DirectoryManager.GetLogFiles(); } catch (Exception l_oException) { @@ -97,7 +97,7 @@ namespace TINK.Model.Logging public static string GetLogFilePath( this ILogger p_oLogger) { - return m_oDirectoryManager.LogFilePath; + return DirectoryManager.LogFilePath; } } } diff --git a/TINKLib/Model/Logging/LoggingDirectoryManager.cs b/TINKLib/Model/Logging/LoggingDirectoryManager.cs index d14b5f6..8cded53 100644 --- a/TINKLib/Model/Logging/LoggingDirectoryManager.cs +++ b/TINKLib/Model/Logging/LoggingDirectoryManager.cs @@ -59,7 +59,7 @@ namespace TINK.Model.Logging if (string.IsNullOrEmpty(LogFileTitle)) { - LogFileTitle = $"{ DateTime.Now:yyyy_MM_dd_HH_mm_ss}.jsnl"; + LogFileTitle = $"{DateTime.Now:yyyy_MM_dd_HH_mm_ss}.jsnl"; } // Create directory if direcotry does not exist. @@ -74,7 +74,6 @@ namespace TINK.Model.Logging throw new FileOperationException($"Logging directory {LogFilePath} could not be created successfully.", l_oException); } - } } diff --git a/TINKLib/Model/Settings/JsonSettingsDictionary.cs b/TINKLib/Model/Settings/JsonSettingsDictionary.cs index ab78b39..dc0d407 100644 --- a/TINKLib/Model/Settings/JsonSettingsDictionary.cs +++ b/TINKLib/Model/Settings/JsonSettingsDictionary.cs @@ -34,6 +34,9 @@ namespace TINK.Model.Settings /// Key of the logging level entry. public const string MINLOGGINGLEVELKEY = "MinimumLoggingLevel"; + /// Key of the logging level entry. + public const string ISREPORTLEVELVERBOSEKEY = "IsReportLevelVerbose"; + /// Key of the center to ... entry. public const string CENTERMAPTOCURRENTLOCATION = "CenterMapToCurrentLocation"; @@ -273,6 +276,16 @@ namespace TINK.Model.Settings return (LogEventLevel)int.Parse(JsonConvert.DeserializeObject(l_strLevel)); } + /// Gets a value indicating whether report level is verbose or not. + /// Dictionary to get value from. + public static bool? GetIsReportLevelVerbose(Dictionary settingsJSON) => GetNullableEntry(ISREPORTLEVELVERBOSEKEY, settingsJSON); + + /// Sets a value indicating whether report level is verbose or not. + /// Dictionary to get value from. + public static Dictionary SetIsReportLevelVerbose(this IDictionary targetDictionary, bool isReportLevelVerbose) + => SetEntry(isReportLevelVerbose, ISREPORTLEVELVERBOSEKEY, targetDictionary); + + /// Sets the logging level. /// Dictionary to get logging level from. public static Dictionary SetMinimumLoggingLevel(this IDictionary p_oTargetDictionary, LogEventLevel p_oLevel) diff --git a/TINKLib/Model/Settings/Settings.cs b/TINKLib/Model/Settings/Settings.cs index 6c7a244..b63bb60 100644 --- a/TINKLib/Model/Settings/Settings.cs +++ b/TINKLib/Model/Settings/Settings.cs @@ -15,6 +15,8 @@ namespace TINK.Model.Settings { public const LogEventLevel DEFAULTLOGGINLEVEL = LogEventLevel.Error; + public const bool DEFAULTREPOTLEVEL = false; + // Default value of the expires after entry. Controls the expiration time of the cache values. private TimeSpan DEFAULTEXPIRESAFTER = TimeSpan.FromSeconds(1); @@ -24,6 +26,7 @@ namespace TINK.Model.Settings /// /// /// Minimum logging level to be applied. + /// True if logging level is verbose. /// Holds the expires after value. /// Gets the name of the lock service to use. /// Timeout to apply when connecting to bluetooth lock @@ -34,6 +37,7 @@ namespace TINK.Model.Settings Uri activeUri = null, PollingParameters pollingParameters = null, LogEventLevel? minimumLogEventLevel = null, + bool? isReportLevelVerbose = null, TimeSpan? expiresAfter = null, string activeLockService = null, TimeSpan? connectTimeout = null, @@ -48,6 +52,7 @@ namespace TINK.Model.Settings ActiveUri = GetActiveUri(activeUri); PollingParameters = pollingParameters ?? PollingParameters.Default; MinimumLogEventLevel = minimumLogEventLevel ?? DEFAULTLOGGINLEVEL; + IsReportLevelVerbose = isReportLevelVerbose ?? DEFAULTREPOTLEVEL; ExpiresAfter = expiresAfter ?? DEFAULTEXPIRESAFTER; ActiveLockService = activeLockService ?? LocksServicesContainerMutable.DefaultLocksservice; ConnectTimeout = connectTimeout ?? new TimeSpan(0, 0, TimeOutProvider.DEFAULT_BLUETOOTHCONNECT_TIMEOUTSECONDS); // Try one sec. to connect. @@ -93,6 +98,9 @@ namespace TINK.Model.Settings public string ActiveTheme { get; } + /// Gets a value indicating whether reporting level is verbose or not. + public bool IsReportLevelVerbose { get; } + public static Uri GetActiveUri(Uri activeUri) => activeUri ?? Services.CopriApi.ServerUris.CopriServerUriList.DefaultActiveUri; public static bool GetCenterMapToCurrentLocation(Uri activeUri) diff --git a/TINKLib/Model/Station/IStation.cs b/TINKLib/Model/Station/IStation.cs index 05cda1d..1476954 100644 --- a/TINKLib/Model/Station/IStation.cs +++ b/TINKLib/Model/Station/IStation.cs @@ -5,7 +5,7 @@ namespace TINK.Model.Station public interface IStation { /// Holds the unique id of the station.c - int Id { get; } + string Id { get; } /// Holds the group to which the station belongs. IEnumerable Group { get; } diff --git a/TINKLib/Model/Station/NullStation.cs b/TINKLib/Model/Station/NullStation.cs index f782971..f68ce46 100644 --- a/TINKLib/Model/Station/NullStation.cs +++ b/TINKLib/Model/Station/NullStation.cs @@ -6,7 +6,7 @@ namespace TINK.Model.Station public class NullStation : IStation { /// Holds the unique id of the station.c - public int Id => -1; + public string Id => null; /// Holds the group to which the station belongs. public IEnumerable Group => new List(); diff --git a/TINKLib/Model/Station/Station.cs b/TINKLib/Model/Station/Station.cs index f35e595..fe30e4d 100644 --- a/TINKLib/Model/Station/Station.cs +++ b/TINKLib/Model/Station/Station.cs @@ -12,7 +12,7 @@ namespace TINK.Model.Station /// GPS- position of the station. /// Name of the station. public Station( - int p_iId, + string p_iId, IEnumerable p_oGroup, Position p_oPosition, string p_strStationName = "") @@ -24,7 +24,7 @@ namespace TINK.Model.Station } /// Holds the unique id of the station.c - public int Id { get; } + public string Id { get; } /// Holds the group to which the station belongs. public IEnumerable Group { get; } diff --git a/TINKLib/Model/Station/StationCollection.cs b/TINKLib/Model/Station/StationCollection.cs index c32f7f5..ca9c4c1 100644 --- a/TINKLib/Model/Station/StationCollection.cs +++ b/TINKLib/Model/Station/StationCollection.cs @@ -4,10 +4,10 @@ using System.Collections.Generic; namespace TINK.Model.Station { - public class StationDictionary : IEnumerable + public class StationDictionary : IEnumerable { /// Holds the list of stations. - private readonly IDictionary m_oStationDictionary; + private readonly IDictionary m_oStationDictionary; /// Count of stations. public int Count { get { return m_oStationDictionary.Count; } } @@ -16,16 +16,16 @@ namespace TINK.Model.Station /// Constructs a station dictionary object. /// Version of copri- service. - public StationDictionary(Version p_oVersion = null, IDictionary p_oStations = null) + public StationDictionary(Version p_oVersion = null, IDictionary p_oStations = null) { - m_oStationDictionary = p_oStations ?? new Dictionary(); + m_oStationDictionary = p_oStations ?? new Dictionary(); CopriVersion = p_oVersion != null ? new Version(p_oVersion.Major, p_oVersion.Minor, p_oVersion.Revision, p_oVersion.Build) : new Version(0, 0, 0, 0); } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return m_oStationDictionary.Values.GetEnumerator(); } @@ -33,41 +33,41 @@ namespace TINK.Model.Station /// /// Deteermines whether a station by given key exists. /// - /// Key to check. + /// Key to check. /// True if station exists. - public bool ContainsKey(int p_strKey) + public bool ContainsKey(string key) { - return m_oStationDictionary.ContainsKey(p_strKey); + return m_oStationDictionary.ContainsKey(key); } /// /// Remove a station by station id. /// - /// - public void RemoveById(int p_iId) + /// + public void RemoveById(string id) { - if (!m_oStationDictionary.ContainsKey(p_iId)) + if (!m_oStationDictionary.ContainsKey(id)) { // Nothing to do if there is no station with given name. return; } - m_oStationDictionary.Remove(p_iId); + m_oStationDictionary.Remove(id); } /// /// Remove a station by station name. /// - /// - public Station GetById(int p_iId) + /// + public IStation GetById(string id) { - if (!m_oStationDictionary.ContainsKey(p_iId)) + if (!m_oStationDictionary.ContainsKey(id)) { // Nothing to do if there is no station with given name. return null; } - return m_oStationDictionary[p_iId]; + return m_oStationDictionary[id]; } /// diff --git a/TINKLib/Model/TinkApp.cs b/TINKLib/Model/TinkApp.cs index ef9cfaf..7a822dc 100644 --- a/TINKLib/Model/TinkApp.cs +++ b/TINKLib/Model/TinkApp.cs @@ -22,6 +22,7 @@ using TINK.ViewModel.Settings; using TINK.Services; using TINK.Services.BluetoothLock.BLE; using Xamarin.Forms; +using TINK.Model.Station; namespace TINK.Model { @@ -34,7 +35,7 @@ namespace TINK.Model /// True if setting credentials succeeded. public delegate bool SetCredentialsDelegate(string p_strMailAddress, string p_strPassword); - /// Returns the id of the app to be identified by copri. + /// Returns the id of the app (sharee.bike) to be identified by copri. public static string MerchantId => "oiF2kahH"; /// @@ -67,6 +68,9 @@ namespace TINK.Model /// Gets the minimum logging level. public LogEventLevel MinimumLogEventLevel { get; set; } + /// Gets a value indicating whether reporting level is verbose or not. + public bool IsReportLevelVerbose { get; set; } + /// Holds the uri which is applied after restart. public Uri NextActiveUri { get; set; } @@ -81,6 +85,7 @@ namespace TINK.Model .SetGroupFilterSettings(FilterGroupSetting) .SetAppVersion(AppVersion) .SetMinimumLoggingLevel(MinimumLogEventLevel) + .SetIsReportLevelVerbose(IsReportLevelVerbose) .SetExpiresAfter(ExpiresAfter) .SetWhatsNew(AppVersion) .SetActiveLockService(LocksServices.Active.GetType().FullName) @@ -115,7 +120,11 @@ namespace TINK.Model /// /// Holds the default polling value. /// +#if USCSHARP9 + public TimeSpan DefaultPolling => new (0, 0, 10); +#else public TimeSpan DefaultPolling => new TimeSpan(0, 0, 10); +#endif /// Constructs TinkApp object. /// @@ -139,10 +148,9 @@ namespace TINK.Model Settings.Settings settings, IStore accountStore, Func connectorFactory, - IGeolocation geolocationService, - IGeolodationDependent geolodationServiceDependent, + IServicesContainer geolocationServicesContainer, ILocksService locksService, - IDevice device, + ISmartDevice device, ISpecialFolder specialFolder, ICipher cipher, IPermissions permissions = null, @@ -188,16 +196,8 @@ namespace TINK.Model new HashSet { new Themes.Konrad() , new Themes.ShareeBike() }, settings.ActiveTheme); - GeolocationServices = new ServicesContainerMutable( - geolocationService == null - ? new HashSet { new LastKnownGeolocationService(geolodationServiceDependent), new SimulatedGeolocationService(geolodationServiceDependent), new GeolocationService(geolodationServiceDependent) } - : new HashSet { geolocationService }, - geolocationService == null - ? (lastVersion >= new Version(3, 0, 173) ? settings.ActiveGeolocationService : typeof(LastKnownGeolocationService).FullName) - : geolocationService.GetType().FullName); - - // Load filters from settings or apply defaults if no settings are available - var l_oAccount = accountStore.Load(); + GeolocationServices = geolocationServicesContainer + ?? throw new ArgumentException($"Can not instantiate {nameof(TinkApp)}- object. No geolocation services container object available."); if (settings.ActiveUri == new Uri(CopriServerUriList.TINK_LIVE) || settings.ActiveUri == new Uri(CopriServerUriList.TINK_DEVEL)) @@ -217,7 +217,7 @@ namespace TINK.Model CenterMapToCurrentLocation = settings.CenterMapToCurrentLocation; - Device = device + SmartDevice = device ?? throw new ArgumentException("Can not instantiate TinkApp- object. No device information provider available."); if (specialFolder == null) @@ -236,12 +236,10 @@ namespace TINK.Model SettingsFileFolder = specialFolder.GetInternalPersonalDir(); - SelectedStation = null; - ActiveUser = new User.User( - accountStore, - l_oAccount, - device.GetIdentifier()); + accountStore.GetType().Name == "StoreLegacy" ? new Store() : accountStore, + accountStore.Load().Result, + device.Identifier); this.isConnectedFunc = isConnectedFunc ?? (() => CrossConnectivity.Current.IsConnected); @@ -249,7 +247,7 @@ namespace TINK.Model // Create filtered connector for offline mode. m_oConnector = FilteredConnectorFactory.Create( - FilterGroupSetting.DoFilter(l_oAccount.DoFilter(GroupFilterMapPage.DoFilter())), + FilterGroupSetting.DoFilter(GroupFilterMapPage.DoFilter()), ConnectorFactory(GetIsConnected(), settings.ActiveUri, ActiveUser.SessionCookie, ActiveUser.Mail, ExpiresAfter)); // Get uris from file. @@ -266,8 +264,7 @@ namespace TINK.Model MinimumLogEventLevel = settings.MinimumLogEventLevel; - Permissions = permissions ?? - throw new ArgumentException("Can not instantiate TinkApp- object. Permissions object must never be null."); + IsReportLevelVerbose = settings.IsReportLevelVerbose; WhatsNew = new WhatsNew(AppVersion, lastVersion, whatsNewShownInVersion); @@ -299,13 +296,10 @@ namespace TINK.Model public User.User ActiveUser { get; } /// Reference of object which provides device information. - public IDevice Device { get; } - - /// Os permission. - public IPermissions Permissions { get; } + public ISmartDevice SmartDevice { get; } /// Holds delegate to determine whether device is connected or not. - private Func isConnectedFunc; + private readonly Func isConnectedFunc; /// Gets whether device is connected to internet or not. public bool GetIsConnected() => isConnectedFunc(); @@ -328,13 +322,16 @@ namespace TINK.Model public ICipher Cipher { get; } /// Name of the station which is selected. - public int? SelectedStation { get; set; } + public IStation SelectedStation { get; set; } = new Station.Station(null, new List(), null); + + /// Holds the stations availalbe. + public IEnumerable Stations { get; set; } = new List(); /// Action to post to GUI thread. public Action PostAction { get; } /// Function which creates a connector depending on connected status. - private Func ConnectorFactory { get; } + private Func ConnectorFactory { get; } /// Holds the object which provides offline data. private IFilteredConnector m_oConnector; @@ -362,14 +359,11 @@ namespace TINK.Model return m_oConnector; } - /// Query geolocation. - public IGeolocation Geolocation => GeolocationServices.Active; - /// Manages the different types of LocksService objects. public LocksServicesContainerMutable LocksServices { get; set; } /// Holds available app themes. - public ServicesContainerMutable GeolocationServices { get; } + public IServicesContainer GeolocationServices { get; } /// Manages the different types of LocksService objects. public ServicesContainerMutable Themes { get; } diff --git a/TINKLib/Model/User/Account/Account.cs b/TINKLib/Model/User/Account/Account.cs index 031ddf1..62ecc32 100644 --- a/TINKLib/Model/User/Account/Account.cs +++ b/TINKLib/Model/User/Account/Account.cs @@ -17,6 +17,7 @@ namespace TINK.Model.User.Account PickLoggingLevel = 64, // Allows to select the logging level. ShowDiagnostics = 128, // Turns on display of diagnostics. SwitchNoSiteCaching = 1024, // Allows to turn off/ on caching of sites displayed in app hosted by COPRI + ReportLevel = 2048, // Allows extent to show error messages. All = PickCopriServer + ManageCopriCacheExpiration + ManagePolling + @@ -24,7 +25,8 @@ namespace TINK.Model.User.Account PickLocationServiceImplementation + PickLoggingLevel + ShowDiagnostics + - SwitchNoSiteCaching, + SwitchNoSiteCaching + + ReportLevel, } /// @@ -48,24 +50,24 @@ namespace TINK.Model.User.Account public class Account : IAccount { /// Constructs an account object. - /// Mail addresss. - /// Password. - /// Session cookie from copri. - /// Group holdig info about Group (TINK, Konrad, ...) + /// Mail addresss. + /// Password. + /// Session cookie from copri. + /// Group holdig info about Group (TINK, Konrad, ...) /// Flag which controls display of debug settings. public Account( - string p_oMail, - string p_Pwd, - string p_oSessionCookie, - IEnumerable p_strGroup, + string mail, + string password, + string sessionCookie, + IEnumerable group, Permissions debugLevel = Permissions.None) { - Mail = p_oMail; - Pwd = p_Pwd; - SessionCookie = p_oSessionCookie; + Mail = mail; + Pwd = password; + SessionCookie = sessionCookie; DebugLevel = debugLevel; - Group = p_strGroup != null - ? new HashSet(p_strGroup).ToList() + Group = group != null + ? new HashSet(group).ToList() : throw new ArgumentException("Can not instantiate account object. Reference to group list must not be empty."); } diff --git a/TINKLib/Model/User/Account/IStore.cs b/TINKLib/Model/User/Account/IStore.cs index 22be440..5cd95c5 100644 --- a/TINKLib/Model/User/Account/IStore.cs +++ b/TINKLib/Model/User/Account/IStore.cs @@ -1,26 +1,26 @@ -namespace TINK.Model.User.Account +using System.Threading.Tasks; + +namespace TINK.Model.User.Account { - /// - /// Interface to manage an account store. - /// + /// Interface to manage an account store. public interface IStore { /// /// Reads mail address and password from account store. /// /// - IAccount Load(); + Task Load(); /// /// Writes mail address and password to account store. /// - /// - void Save(IAccount p_oMailAndPwd); + /// + Task Save(IAccount mailAndPwd); /// /// Deletes mail address and password from account store. /// /// Empty account instance if deleting succeeded. - IAccount Delete(IAccount p_oMailAndPwd); + IAccount Delete(IAccount mailAndPwd); } } diff --git a/TINKLib/Model/User/Account/Store.cs b/TINKLib/Model/User/Account/Store.cs new file mode 100644 index 0000000..d7db191 --- /dev/null +++ b/TINKLib/Model/User/Account/Store.cs @@ -0,0 +1,67 @@ +using Serilog; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xamarin.Essentials; + +namespace TINK.Model.User.Account +{ + public class Store : IStore + { + /// Holds id of the debug level key. + private const string KEY_DEBUGLEVEL = "DebugLevel"; + + /// Holds the id of the session. + private const string KEY_SESSIONCOOKIE = "SessionCookie"; + + /// Holds id of the mail address key. + private const string KEY_MAILADDRESS = "MailAddress"; + + public IAccount Delete(IAccount account) + { + SecureStorage.RemoveAll(); + return new EmptyAccount(); + } + + public async Task Load() + { + + var mail = string.Empty; + var sessionCookie = string.Empty; + var debugLevel = Permissions.None; + + try + { + mail = await SecureStorage.GetAsync(KEY_MAILADDRESS); + sessionCookie = await SecureStorage.GetAsync(KEY_SESSIONCOOKIE); + Enum.TryParse(await SecureStorage.GetAsync(KEY_DEBUGLEVEL), out debugLevel); + + } + catch (Exception exception) + { + Log.ForContext().Error("Loading account from store failed. {Exception}", exception); + } + + return new Account( + mail, + string.Empty, + sessionCookie, + new List(), + debugLevel); + } + + public async Task Save(IAccount mailAndPwd) + { + try + { + await SecureStorage.SetAsync(KEY_MAILADDRESS, mailAndPwd.Mail); + await SecureStorage.SetAsync(KEY_SESSIONCOOKIE, mailAndPwd.SessionCookie); + await SecureStorage.SetAsync(KEY_DEBUGLEVEL, mailAndPwd.DebugLevel.ToString()); + } + catch (Exception exception) + { + Log.ForContext().Error("Saving account from store failed. {Exception}", exception); + } + } + } +} diff --git a/TINKLib/Model/User/User.cs b/TINKLib/Model/User/User.cs index 8cfccb0..6d1b079 100644 --- a/TINKLib/Model/User/User.cs +++ b/TINKLib/Model/User/User.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using TINK.Model.User.Account; namespace TINK.Model.User @@ -29,14 +30,14 @@ namespace TINK.Model.User /// /// Object to use for loading and saving user data. public User( - IStore p_oAccountStore, - IAccount p_oAccount, - string p_strDeviceId) + IStore accountStore, + IAccount account, + string deviceId) { - m_oStore = p_oAccountStore + m_oStore = accountStore ?? throw new ArgumentException("Can not instantiate user- object. No store functionality available."); - DeviceId = p_strDeviceId; - m_oAccount = new AccountMutable(p_oAccount); + DeviceId = deviceId; + m_oAccount = new AccountMutable(account); } /// Is fired wheneverlogin state changes. @@ -109,13 +110,13 @@ namespace TINK.Model.User /// Account to use for login. /// Holds the Id to identify the device. /// True if connector has access to copri server, false if cached values are used. - public void Login(IAccount account) + public async Task Login(IAccount account) { // Update account instance from copri data. m_oAccount.Copy(account); // Save data to store. - m_oStore.Save(m_oAccount); + await m_oStore.Save(m_oAccount); // Nothing to do because state did not change. StateChanged?.Invoke(this, new EventArgs()); diff --git a/TINKLib/Model/WhatsNew.cs b/TINKLib/Model/WhatsNew.cs index 0ecf0d5..f0756c5 100644 --- a/TINKLib/Model/WhatsNew.cs +++ b/TINKLib/Model/WhatsNew.cs @@ -316,7 +316,7 @@ namespace TINK.Model }, { new Version(3, 0, 208), - AppResources.ChangeLog3_0_208 + AppResources.ChangeLog3_0_208 // Minor fixes. }, { new Version(3, 0, 209), @@ -340,7 +340,7 @@ namespace TINK.Model }, { new Version(3, 0, 218), - AppResources.ChangeLog3_0_218 + AppResources.ChangeLog3_0_208 }, { new Version(3, 0, 219), @@ -353,8 +353,61 @@ namespace TINK.Model { new Version(3, 0, 222), AppResources.ChangeLog3_0_222 + }, + { + new Version(3, 0, 223), + AppResources.ChangeLog3_0_208 + }, + { + new Version(3, 0, 224), + AppResources.ChangeLog3_0_224 + }, + { + new Version(3, 0, 225), + AppResources.ChangeLog3_0_208 + }, + { + new Version(3, 0, 226), + AppResources.ChangeLog3_0_226 + }, + { + new Version(3, 0, 227), + AppResources.ChangeLog3_0_227 + }, + { + new Version(3, 0, 228), + AppResources.ChangeLog3_0_208 + }, + { + new Version(3, 0, 231), + AppResources.ChangeLog3_0_231 + }, + { + new Version(3, 0, 232), + AppResources.ChangeLog3_0_232 + }, + { + new Version(3, 0, 234), + AppResources.ChangeLog3_0_234 + }, + { + new Version(3, 0, 235), + AppResources.ChangeLog3_0_235 + }, + { + new Version(3, 0, 236), + AppResources.ChangeLog3_0_236 + }, + { + new Version(3, 0, 237), + AppResources.ChangeLog3_0_237 + }, + { + new Version(3, 0, 238), + AppResources.ChangeLog3_0_231 } + }; /// Manges the whats new information. diff --git a/TINKLib/MultilingualResources/AppResources.Designer.cs b/TINKLib/MultilingualResources/AppResources.Designer.cs index 628b74b..c345594 100644 --- a/TINKLib/MultilingualResources/AppResources.Designer.cs +++ b/TINKLib/MultilingualResources/AppResources.Designer.cs @@ -222,6 +222,15 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Offline.. + /// + public static string ActivityTextConnectionStateOffline { + get { + return ResourceManager.GetString("ActivityTextConnectionStateOffline", resourceCulture); + } + } + /// /// Looks up a localized string similar to Disconnecting lock.... /// @@ -322,11 +331,20 @@ namespace TINK.MultilingualResources { } /// - /// Looks up a localized string similar to Connection error. Code: {0}.. + /// Looks up a localized string similar to Connection error. Status code: {0}.. /// - public static string ActivityTextErrorWebException { + public static string ActivityTextErrorWebExceptionGeneralError { get { - return ResourceManager.GetString("ActivityTextErrorWebException", resourceCulture); + return ResourceManager.GetString("ActivityTextErrorWebExceptionGeneralError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connection error. Status description: {0}.. + /// + public static string ActivityTextErrorWebExceptionProtocolError { + get { + return ResourceManager.GetString("ActivityTextErrorWebExceptionProtocolError", resourceCulture); } } @@ -339,6 +357,15 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Cache used.. + /// + public static string ActivityTextException { + get { + return ResourceManager.GetString("ActivityTextException", resourceCulture); + } + } + /// /// Looks up a localized string similar to Loading Stations and Bikes.... /// @@ -456,6 +483,15 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Updateing..... + /// + public static string ActivityTextUpdating { + get { + return ResourceManager.GetString("ActivityTextUpdating", resourceCulture); + } + } + /// /// Looks up a localized string similar to Updated to latest lock firmware.. /// @@ -557,15 +593,6 @@ namespace TINK.MultilingualResources { } } - /// - /// Looks up a localized string similar to Minor fixes.. - /// - public static string ChangeLog3_0_218 { - get { - return ResourceManager.GetString("ChangeLog3_0_218", resourceCulture); - } - } - /// /// Looks up a localized string similar to Icons added to flyout menu.. /// @@ -602,6 +629,92 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Updated for latest server version.. + /// + public static string ChangeLog3_0_224 { + get { + return ResourceManager.GetString("ChangeLog3_0_224", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fixes + ///- station markers are updated correctly after login/ logout if required + ///- routing from "bikes at station" to "login" page and other routings corrected. + /// + public static string ChangeLog3_0_226 { + get { + return ResourceManager.GetString("ChangeLog3_0_226", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Feedback dialog added which is shown after returning bike.. + /// + public static string ChangeLog3_0_227 { + get { + return ResourceManager.GetString("ChangeLog3_0_227", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Minor improvements.. + /// + public static string ChangeLog3_0_231 { + get { + return ResourceManager.GetString("ChangeLog3_0_231", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Menu modified.. + /// + public static string ChangeLog3_0_232 { + get { + return ResourceManager.GetString("ChangeLog3_0_232", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tariff info page is displayed correctly again. + ///Error handling improved.. + /// + public static string ChangeLog3_0_234 { + get { + return ResourceManager.GetString("ChangeLog3_0_234", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Obsolete object replaced with up-to-date one.. + /// + public static string ChangeLog3_0_235 { + get { + return ResourceManager.GetString("ChangeLog3_0_235", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stations names instead descriptions shown on page title. + ///Error message when opening lock fails improved. + ///Layout of bike names and id display improved.. + /// + public static string ChangeLog3_0_236 { + get { + return ResourceManager.GetString("ChangeLog3_0_236", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hard- and software information send to backend when returning bike.. + /// + public static string ChangeLog3_0_237 { + get { + return ResourceManager.GetString("ChangeLog3_0_237", resourceCulture); + } + } + /// /// Looks up a localized string similar to Lock of rented bike can not be found.. /// @@ -688,9 +801,18 @@ namespace TINK.MultilingualResources { /// /// Looks up a localized string similar to Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.. /// - public static string ErrorOpenLockMessage { + public static string ErrorOpenLockBoldBlockedMessage { get { - return ResourceManager.GetString("ErrorOpenLockMessage", resourceCulture); + return ResourceManager.GetString("ErrorOpenLockBoldBlockedMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again.. + /// + public static string ErrorOpenLockBoldWasBlockedMessage { + get { + return ResourceManager.GetString("ErrorOpenLockBoldWasBlockedMessage", resourceCulture); } } @@ -715,6 +837,15 @@ namespace TINK.MultilingualResources { /// /// Looks up a localized string similar to Lock can not be opened!. /// + public static string ErrorOpenLockStillOpenTitle { + get { + return ResourceManager.GetString("ErrorOpenLockStillOpenTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error while opening lock!. + /// public static string ErrorOpenLockTitle { get { return ResourceManager.GetString("ErrorOpenLockTitle", resourceCulture); @@ -770,6 +901,24 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Your feedback could not be send to server successfully.. + /// + public static string ErrorReturnSubmitFeedbackMessage { + get { + return ResourceManager.GetString("ErrorReturnSubmitFeedbackMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Submitting feedback failed!. + /// + public static string ErrorReturnSubmitFeedbackTitle { + get { + return ResourceManager.GetString("ErrorReturnSubmitFeedbackTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Attachment could not be created.. /// @@ -871,15 +1020,6 @@ namespace TINK.MultilingualResources { } } - /// - /// Looks up a localized string similar to Bike Location {0}. - /// - public static string MarkingBikesAtStationTitle { - get { - return ResourceManager.GetString("MarkingBikesAtStationTitle", resourceCulture); - } - } - /// /// Looks up a localized string similar to Contact. /// @@ -1207,6 +1347,16 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to This version of the {0} App is not compatible with server detected. + ///Please contact the support for help.. + /// + public static string MessageCopriVersionIsOutdated { + get { + return ResourceManager.GetString("MessageCopriVersionIsOutdated", resourceCulture); + } + } + /// /// Looks up a localized string similar to Attention: Lock is closed! ///{0} @@ -1247,7 +1397,7 @@ namespace TINK.MultilingualResources { } /// - /// Looks up a localized string similar to Error during login!. + /// Looks up a localized string similar to Error while logging in!. /// public static string MessageLoginErrorTitle { get { @@ -1300,6 +1450,15 @@ namespace TINK.MultilingualResources { } } + /// + /// Looks up a localized string similar to Error while logging out!. + /// + public static string MessageLogoutErrorTitle { + get { + return ResourceManager.GetString("MessageLogoutErrorTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Session has expired. ///Either there are more than 8 devices in use or the user's account is no longer valid. diff --git a/TINKLib/MultilingualResources/AppResources.de.resx b/TINKLib/MultilingualResources/AppResources.de.resx index bb76d3b..594197b 100644 --- a/TINKLib/MultilingualResources/AppResources.de.resx +++ b/TINKLib/MultilingualResources/AppResources.de.resx @@ -61,7 +61,7 @@ Kein Benutzer angemeldet. - Diese version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren. + Diese Version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren. Betrifft die Anfrage/ Anmerkung die {0}-App oder ein allgemeines Thema? @@ -99,9 +99,6 @@ Tarife - - Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen. - Schloss ist blockiert. Bitte sicherstellen, dass keine Speiche oder ein anderer Gegenstand das Schloss blockiert und Vorgang wiederholen. @@ -190,9 +187,6 @@ Entweder es sind mehr als 8 Geräte in Benutzung oder das Konto ist nicht mehr g Die Nutzung der App ist auf maximal 8 Geräten pro Konto möglich. Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen, ob das Konto noch gültig ist. - - Fahrradstandort {0} - Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen. @@ -296,9 +290,6 @@ Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen Schloss kann erst geöffnet werden, wenn Rad in der Nähe ist. - - Schloss kann nicht geöffnet werden! - Schloss kann erst geschlossen werden, wenn das Rad in der Nähe ist. @@ -480,9 +471,6 @@ Bitte App neu starten um Rad Infos zu bekommen. Verbindung unterbrochen, Server nicht erreichbar. - - Verbindungsfehler. Code: {0}. - Verbindung unterbrochen, Server beschäftigt. @@ -528,9 +516,6 @@ Bitte App neu starten um Rad Infos zu bekommen. Pakete aktualisiert. - - Kleine Verbesserungen. - Icons zum Flyout-Menü hinzugefügt. @@ -543,4 +528,77 @@ Bitte App neu starten um Rad Infos zu bekommen. Kleine Fehlerbehebungen und Verbesserungen. + + Fehler beim Abmelden! + + + Diese Version der {0} App ist nicht kompatibel mit dem erkannten Server. +Bitte kontaktieren sie den Support! + + + Aktualisiert auf aktuelle Serverversion. + + + Fehlerbehebungen +- Stationssymbole werden nach dem An-/ Abmelden aktualisiert +- Navigation von "Fahrradstandort X" auf "Anmelen"- Seite und andere Verknüpfungen korrigiert + + + + Verbindungsfehler. Statusbeschreibung: {0}. + + + Verbindungsfehler. Status statucode: {0}. + + + Seite zur Eingabe von Rückmeldungen hinzugefügt, der nach Rückgabe eines Rads angezeigt wird. + + + Offline. + + + Cache Daten. + + + Ihre Rückmeldung konnte nicht an den Server übermittelt werden. + + + Übermittlung der Rückmeldung fehlgeschlagen! + + + Aktualisiere.... + + + Kleine Verbesserungen. + + + Menu geändert. + + + Tarifinfo-Seite wird wieder korrekt angezeigt. +Fehlerhandling verbessert. + + + Veraltetes Objekt durch aktuelles ersetzt. + + + Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen. + + + Schloss war oder ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen. + + + Fehler beim Schlossöffnen! + + + Schloss kann nicht geöffnet werden! + + + Anzeige von Stationsnamen statt Nummern. +Fehlermeldung beim Öffnen von Schloss verbessert. +Layout Anzeige Radnamen und nummern verbessert. + + + Hard- und software information werden an Backend übermittelt bei Radrückgabe. + \ No newline at end of file diff --git a/TINKLib/MultilingualResources/AppResources.resx b/TINKLib/MultilingualResources/AppResources.resx index 601c72b..387ee3c 100644 --- a/TINKLib/MultilingualResources/AppResources.resx +++ b/TINKLib/MultilingualResources/AppResources.resx @@ -156,9 +156,6 @@ Lock can only be closed if bike is not moving. Please park bike and try again. - - Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again. - Lock of reserved bike can not be found. @@ -193,9 +190,6 @@ Bike can only be returned if bike is in reach and location information is avail Account - - Bike Location {0} - Contact @@ -378,7 +372,7 @@ Please login to app once again. In case this fails please check on website if th Connection error during registration! - Error during login! + Error while logging in! Please connect to Internet to recover the password. @@ -396,7 +390,7 @@ Please login to app once again. In case this fails please check on website if th Lock cannot be opened until bike is near. - Lock can not be opened! + Error while opening lock! Lock cannot be closed until bike is near. @@ -577,8 +571,8 @@ Please restart app in order to get bike info. Connection interrupted, server unreachable. - - Connection error. Code: {0}. + + Connection error. Status code: {0}. Connection interrupted, server busy. @@ -625,9 +619,6 @@ Please restart app in order to get bike info. Packages updated. - - Minor fixes. - Icons added to flyout menu. @@ -640,4 +631,70 @@ Please restart app in order to get bike info. Minor bugfix and improvements. + + Error while logging out! + + + This version of the {0} App is not compatible with server detected. +Please contact the support for help. + + + Updated for latest server version. + + + Fixes +- station markers are updated correctly after login/ logout if required +- routing from "bikes at station" to "login" page and other routings corrected + + + Connection error. Status description: {0}. + + + Feedback dialog added which is shown after returning bike. + + + Offline. + + + Cache used. + + + Your feedback could not be send to server successfully. + + + Submitting feedback failed! + + + Updateing.... + + + Minor improvements. + + + Menu modified. + + + Tariff info page is displayed correctly again. +Error handling improved. + + + Obsolete object replaced with up-to-date one. + + + Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again. + + + Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again. + + + Lock can not be opened! + + + Stations names instead descriptions shown on page title. +Error message when opening lock fails improved. +Layout of bike names and id display improved. + + + Hard- and software information send to backend when returning bike. + \ No newline at end of file diff --git a/TINKLib/MultilingualResources/TINKLib.de.xlf b/TINKLib/MultilingualResources/TINKLib.de.xlf index bbe5646..d8cfb38 100644 --- a/TINKLib/MultilingualResources/TINKLib.de.xlf +++ b/TINKLib/MultilingualResources/TINKLib.de.xlf @@ -72,7 +72,7 @@ This version of the {0} App is outdated. Please update to the latest version. - Diese version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren. + Diese Version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren. Does your request/ comment relate to the {0}-app or to a more general subject? @@ -122,10 +122,6 @@ Pricing Tarife - - Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again. - Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen. - Lock is blocked. Please ensure that no spoke or any other obstacle prevents the lock from closing and try again. Schloss ist blockiert. Bitte sicherstellen, dass keine Speiche oder ein anderer Gegenstand das Schloss blockiert und Vorgang wiederholen. @@ -248,10 +244,6 @@ Entweder es sind mehr als 8 Geräte in Benutzung oder das Konto ist nicht mehr g Die Nutzung der App ist auf maximal 8 Geräten pro Konto möglich. Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen, ob das Konto noch gültig ist. - - Bike Location {0} - Fahrradstandort {0} - Code {0}, max. reservation time of {1} minutes expired. Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen. @@ -365,7 +357,7 @@ Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen Verbindungsfehler beim Registrieren! - Error during login! + Error while logging in! Fehler bei der Anmeldung! @@ -388,10 +380,6 @@ Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen Lock cannot be opened until bike is near. Schloss kann erst geöffnet werden, wenn Rad in der Nähe ist. - - Lock can not be opened! - Schloss kann nicht geöffnet werden! - Lock cannot be closed until bike is near. Schloss kann erst geschlossen werden, wenn das Rad in der Nähe ist. @@ -640,10 +628,6 @@ Bitte App neu starten um Rad Infos zu bekommen. Connection interrupted, server unreachable. Verbindung unterbrochen, Server nicht erreichbar. - - Connection error. Code: {0}. - Verbindungsfehler. Code: {0}. - Connection interrupted, server busy. Verbindung unterbrochen, Server beschäftigt. @@ -704,10 +688,6 @@ Bitte App neu starten um Rad Infos zu bekommen. Packages updated. Pakete aktualisiert. - - Minor fixes. - Kleine Verbesserungen. - Icons added to flyout menu. Icons zum Flyout-Menü hinzugefügt. @@ -724,6 +704,107 @@ Bitte App neu starten um Rad Infos zu bekommen. Minor bugfix and improvements. Kleine Fehlerbehebungen und Verbesserungen. + + Error while logging out! + Fehler beim Abmelden! + + + This version of the {0} App is not compatible with server detected. +Please contact the support for help. + Diese Version der {0} App ist nicht kompatibel mit dem erkannten Server. +Bitte kontaktieren sie den Support! + + + Updated for latest server version. + Aktualisiert auf aktuelle Serverversion. + + + Fixes +- station markers are updated correctly after login/ logout if required +- routing from "bikes at station" to "login" page and other routings corrected + Fehlerbehebungen +- Stationssymbole werden nach dem An-/ Abmelden aktualisiert +- Navigation von "Fahrradstandort X" auf "Anmelen"- Seite und andere Verknüpfungen korrigiert + + + + Connection error. Status description: {0}. + Verbindungsfehler. Statusbeschreibung: {0}. + + + Connection error. Status code: {0}. + Verbindungsfehler. Status statucode: {0}. + + + Feedback dialog added which is shown after returning bike. + Seite zur Eingabe von Rückmeldungen hinzugefügt, der nach Rückgabe eines Rads angezeigt wird. + + + Offline. + Offline. + + + Cache used. + Cache Daten. + + + Your feedback could not be send to server successfully. + Ihre Rückmeldung konnte nicht an den Server übermittelt werden. + + + Submitting feedback failed! + Übermittlung der Rückmeldung fehlgeschlagen! + + + Updateing.... + Aktualisiere.... + + + Minor improvements. + Kleine Verbesserungen. + + + Menu modified. + Menu geändert. + + + Tariff info page is displayed correctly again. +Error handling improved. + Tarifinfo-Seite wird wieder korrekt angezeigt. +Fehlerhandling verbessert. + + + Obsolete object replaced with up-to-date one. + Veraltetes Objekt durch aktuelles ersetzt. + + + Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again. + Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen. + + + Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again. + Schloss war oder ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen. + + + Error while opening lock! + Fehler beim Schlossöffnen! + + + Lock can not be opened! + Schloss kann nicht geöffnet werden! + + + Stations names instead descriptions shown on page title. +Error message when opening lock fails improved. +Layout of bike names and id display improved. + Anzeige von Stationsnamen statt Nummern. +Fehlermeldung beim Öffnen von Schloss verbessert. +Layout Anzeige Radnamen und nummern verbessert. + + + Hard- and software information send to backend when returning bike. + Hard- und software information werden an Backend übermittelt bei Radrückgabe. + diff --git a/TINKLib/Repository/CopriCallsHttps.cs b/TINKLib/Repository/CopriCallsHttps.cs index 0f8b202..951f923 100644 --- a/TINKLib/Repository/CopriCallsHttps.cs +++ b/TINKLib/Repository/CopriCallsHttps.cs @@ -5,13 +5,13 @@ using System.IO; using System.Net; using System.Text; using System.Threading.Tasks; -using TINK.Model.Repository.Exception; -using TINK.Model.Repository.Request; -using TINK.Model.Repository.Response; -using TINK.Model.Logging; +using TINK.Repository.Exception; +using TINK.Repository.Request; using TINK.Repository.Response; +using TINK.Model.Logging; +using TINK.Model.Device; -namespace TINK.Model.Repository +namespace TINK.Repository { /// Object which manages calls to copri. public class CopriCallsHttps : ICopriServer @@ -20,17 +20,17 @@ namespace TINK.Model.Repository private IRequestBuilder requestBuilder; /// Initializes a instance of the copri calls https object. - /// Host to connect to. - /// Id of the merchant. + /// Host to connect to. + /// Id of the merchant. /// Holds the name and version of the TINKApp. /// Session cookie if user is logged in, null otherwise. public CopriCallsHttps( - Uri p_oCopriHost, - string p_strMerchantId, + Uri copriHost, + string merchantId, string userAgent, string sessionCookie = null) { - m_oCopriHost = p_oCopriHost + m_oCopriHost = copriHost ?? throw new System.Exception($"Can not construct {GetType().ToString()}- object. Uri of copri host must not be null."); UserAgent = !string.IsNullOrEmpty(userAgent) @@ -38,8 +38,8 @@ namespace TINK.Model.Repository : throw new System.Exception($"Can not construct {GetType().ToString()}- object. User agent must not be null or empty."); requestBuilder = string.IsNullOrEmpty(sessionCookie) - ? new RequestBuilder(p_strMerchantId) as IRequestBuilder - : new RequestBuilderLoggedIn(p_strMerchantId, sessionCookie); + ? new RequestBuilder(merchantId) as IRequestBuilder + : new RequestBuilderLoggedIn(merchantId, sessionCookie); } /// Holds the URL for rest calls. @@ -108,14 +108,15 @@ namespace TINK.Model.Repository /// List of files. public async Task GetStationsAsync() { - return await GetStationsAsync(m_oCopriHost.AbsoluteUri, requestBuilder.GetStations(), UserAgent); + var stations = await GetStationsAsync(m_oCopriHost.AbsoluteUri, requestBuilder.GetStations(), UserAgent); + return stations; } /// Get authentication keys. /// Id of the bike to get keys for. /// Response holding authentication keys. - public async Task GetAuthKeys(int bikeId) - => await GetAuthKeysAsync(m_oCopriHost.AbsoluteUri, requestBuilder.CalculateAuthKeys(bikeId), UserAgent); + public async Task GetAuthKeys(string bikeId) + => await GetAuthKeysAsync(m_oCopriHost.AbsoluteUri, requestBuilder.CalculateAuthParameters(bikeId), UserAgent); /// Gets booking request response. @@ -123,7 +124,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Booking response. public async Task DoReserveAsync( - int bikeId, + string bikeId, Uri operatorUri) { return await DoReserveAsync( @@ -137,7 +138,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response on cancel booking request. public async Task DoCancelReservationAsync( - int bikeId, + string bikeId, Uri operatorUri) { return await DoCancelReservationAsync( @@ -151,11 +152,11 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response holding authentication keys. public async Task CalculateAuthKeysAsync( - int bikeId, + string bikeId, Uri operatorUri) => await GetAuthKeysAsync( operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri, - requestBuilder.CalculateAuthKeys(bikeId), + requestBuilder.CalculateAuthParameters(bikeId), UserAgent); /// Updates lock state for a booked bike. @@ -166,7 +167,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response on updating locking state. public async Task UpdateLockingStateAsync( - int bikeId, + string bikeId, LocationDto location, lock_state state, double batteryLevel, @@ -185,7 +186,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Requst on booking request. public async Task DoBookAsync( - int bikeId, + string bikeId, Guid guid, double batteryPercentage, Uri operatorUri) @@ -199,31 +200,35 @@ namespace TINK.Model.Repository /// Returns a bike. /// Id of the bike to return. /// Geolocation of lock. + /// Provides info about hard and software. /// Holds the uri of the operator or null, in case of single operator setup. /// Response on returning request. public async Task DoReturn( - int bikeId, + string bikeId, LocationDto location, + ISmartDevice smartDevice, Uri operatorUri) { return await DoReturn( operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri, - requestBuilder.DoReturn(bikeId, location), + requestBuilder.DoReturn(bikeId, location, smartDevice), UserAgent); } /// Submits feedback to copri server. + /// Id of the bike to which the feedback is related to. /// True if bike is broken. /// General purpose message or error description. /// Holds the uri of the operator or null, in case of single operator setup. /// Response on submitting feedback request. public async Task DoSubmitFeedback( + string bikeId, string message, bool isBikeBroken, Uri operatorUri) => await DoSubmitFeedback( operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri, - requestBuilder.DoSubmitFeedback(message, isBikeBroken), + requestBuilder.DoSubmitFeedback(bikeId, message, isBikeBroken), UserAgent); /// Logs user in. @@ -238,10 +243,10 @@ namespace TINK.Model.Repository { #if !WINDOWS_UWP /// Extract session cookie from response. - string l_oResponseText = string.Empty; + string response = string.Empty; try { - l_oResponseText = await PostAsync( + response = await PostAsync( copriHost, command, userAgent, @@ -262,7 +267,7 @@ namespace TINK.Model.Repository throw; } - return JsonConvert.DeserializeObject>(l_oResponseText)?.tinkjson; + return CopriCallsStatic.DeserializeResponse(response, (version) => new UnsupportedCopriVersionDetectedException()); #else return null; #endif @@ -299,7 +304,7 @@ namespace TINK.Model.Repository } /// Extract session cookie from response. - return JsonConvert.DeserializeObject>(l_oLogoutResponse)?.tinkjson; + return CopriCallsStatic.DeserializeResponse(l_oLogoutResponse, (version) => new UnsupportedCopriVersionDetectedException()); #else return null; #endif @@ -317,10 +322,10 @@ namespace TINK.Model.Repository string userAgent = null) { #if !WINDOWS_UWP - string l_oStationsAllResponse; + string response; try { - l_oStationsAllResponse = await PostAsync(p_strCopriHost, p_oCommand, userAgent); + response = await PostAsync(p_strCopriHost, p_oCommand, userAgent); } catch (System.Exception l_oException) { @@ -338,7 +343,9 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(l_oStationsAllResponse)?.tinkjson; + return CopriCallsStatic.DeserializeResponse( + response, + (version) => CopriCallsMonkeyStore.GetEmptyStationsAllResponse(version)); #else return null; #endif @@ -354,10 +361,10 @@ namespace TINK.Model.Repository string userAgent = null) { #if !WINDOWS_UWP - string l_oBikesAvaialbeResponse; + string response; try { - l_oBikesAvaialbeResponse = await PostAsync(p_strCopriHost, l_oCommand, userAgent); + response = await PostAsync(p_strCopriHost, l_oCommand, userAgent); } catch (System.Exception l_oException) { @@ -375,7 +382,10 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return CopriCallsStatic.DeserializeBikesAvailableResponse(l_oBikesAvaialbeResponse); + return CopriCallsStatic.DeserializeResponse( + response, + (version) => CopriCallsMonkeyStore.GetEmptyBikesAvailableResponse(version)); + #else return null; #endif @@ -391,10 +401,10 @@ namespace TINK.Model.Repository string userAgent = null) { #if !WINDOWS_UWP - string l_oBikesOccupiedResponse; + string response; try { - l_oBikesOccupiedResponse = await PostAsync(p_strCopriHost, p_oCommand, userAgent); + response = await PostAsync(p_strCopriHost, p_oCommand, userAgent); } catch (System.Exception l_oException) { @@ -412,7 +422,9 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return CopriCallsStatic.DeserializeBikesOccupiedResponse(l_oBikesOccupiedResponse); + return CopriCallsStatic.DeserializeResponse( + response, + (version) => CopriCallsMonkeyStore.GetEmptyBikesReservedOccupiedResponse(version)); #else return null; #endif @@ -449,7 +461,7 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(l_oBikesAvaialbeResponse)?.tinkjson; + return JsonConvertRethrow.DeserializeObject>(l_oBikesAvaialbeResponse)?.shareejson; #else return null; #endif @@ -485,7 +497,7 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(l_oBikesAvaialbeResponse)?.tinkjson; + return JsonConvertRethrow.DeserializeObject>(l_oBikesAvaialbeResponse)?.shareejson; #else return null; #endif @@ -522,7 +534,7 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(l_oBikesAvaialbeResponse)?.tinkjson; + return JsonConvertRethrow.DeserializeObject>(l_oBikesAvaialbeResponse)?.shareejson; #else return null; #endif @@ -555,7 +567,7 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(l_oBikesAvaialbeResponse)?.tinkjson; + return JsonConvertRethrow.DeserializeObject>(l_oBikesAvaialbeResponse)?.shareejson; #else return null; #endif @@ -588,7 +600,7 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(l_oBikesAvaialbeResponse)?.tinkjson; + return JsonConvertRethrow.DeserializeObject>(l_oBikesAvaialbeResponse)?.shareejson; #else return null; #endif @@ -621,7 +633,7 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(l_oBikesAvaialbeResponse)?.tinkjson; + return JsonConvertRethrow.DeserializeObject>(l_oBikesAvaialbeResponse)?.shareejson; #else return null; #endif @@ -654,7 +666,7 @@ namespace TINK.Model.Repository } // Extract bikes from response. - return JsonConvert.DeserializeObject>(userFeedbackResponse)?.tinkjson; + return JsonConvertRethrow.DeserializeObject>(userFeedbackResponse)?.shareejson; #else return null; #endif @@ -775,9 +787,9 @@ namespace TINK.Model.Repository return null; #endif } - catch (System.Exception l_oException) + catch (System.Exception exception) { - Log.ForContext().InformationOrError("Posting command {DisplayCommand} to host {URL} failed. {Exception}.", displayCommandFunc(), uRL, l_oException); + Log.ForContext().InformationOrError("Posting command {DisplayCommand} to host {URL} failed. {Exception}.", displayCommandFunc(), uRL, exception); throw; } } diff --git a/TINKLib/Repository/CopriCallsMemory.cs b/TINKLib/Repository/CopriCallsMemory.cs new file mode 100644 index 0000000..4e0c3e0 --- /dev/null +++ b/TINKLib/Repository/CopriCallsMemory.cs @@ -0,0 +1,1314 @@ +using System; +using System.Threading.Tasks; +using TINK.Model; +using TINK.Model.Device; +using TINK.Repository.Request; +using TINK.Repository.Response; + +namespace TINK.Repository +{ + public class CopriCallsMemory : ICopriServer + { + /// Part in file specifying apiserver. + private static string CopriDevelHostUri = @"https://tinkwwp.copri-bike.de/APIjsonserver"; + + public const string DO_AUTH_Unknown_User_FILE = @" + { + ""shareejson"" : { + ""response"" : ""authorization"", + ""authcookie"" : 0, + ""response_state"" : ""Failure: cannot generate authcookie"", + ""apiserver"" : ""https://tinkwwp.copri-bike.de"" + } + }"; + + public const string DO_AUTH_SET02_001_FILE = @" + { + ""shareejson"" : { + ""response"" : ""authorization"", + ""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51a"", + ""user_group"" : [""TINK""], + ""response_state"" : ""OK"", + ""apiserver"" : ""https://tinkwwp.copri-bike.de"" + } + }"; + + public const string DO_AUTHOUT_SET02_001_FILE = @"{ + ""shareejson"" : { + ""apiserver"" : ""https://app.tink-konstanz.de"", + ""user_id"" : ""ohauff@posteo.de"", + ""response_state"" : ""OK"", + ""authcookie"" : ""1"", + ""response"" : ""authout"", + ""user_group"" : [ """" ], + ""debuglevel"" : ""1"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + /// + /// Station TINK: 4, 5, 7, 8, 10, 11, 12, 31 + /// Station Konrad: 14, 31 + /// Stations: + /// 4. Available: 26 Trike, 14, Cargo, 5 Cargo + /// 5. Available: 45 Cargo, 11 Cargo T, 2 Cargo, 52 Sr + /// 7. Available: 20 Cargo, + /// 8. Available: 19 Cargo + /// 10. Available: 18 Cargo + /// 11. Available: 17 Cargo + /// 12. Available: 13 Cargo + /// + /// + /// + public const string STATIONS_SET02_001_FILE = @" + { + ""shareejson"": { + ""authcookie"": 0, + ""apiserver"": ""https://tinkwwp.copri-bike.de"", + ""response"": ""stations_all"", + ""stations"": { + ""5786"": { + ""station"" : ""4"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.6586936667"", ""longitude"": ""9.16863116667"" } + }, + ""2456"": { + ""station"" : ""5"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.6675153333"", ""longitude"": ""9.16473216667"" } + }, + ""2798"": { + ""station"" : ""12"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.6748671667"", ""longitude"": ""9.17326583333"" } + }, + ""3214"": { + ""station"" : ""8"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.666401"", ""longitude"": ""9.175902"" } + }, + ""5462"": { + ""station"" : ""11"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.672231"", ""longitude"": ""9.175862"" } + }, + ""3965"": { + ""station"" : ""10"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.665556"", ""longitude"": ""9.17095133333"" } + }, + ""5423"": { + ""station"" : ""7"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.6612413333"", ""longitude"": ""9.16641016667"" } + }, + ""14"": { + ""station_group"" : [ ""Konrad"" ], + ""description"": """", + ""station"" : ""14"", + ""gps"" : { ""latitude"": ""47.66698054007847"", ""longitude"": ""9.169303178787231"" } + }, + ""31"": { + ""station_group"": [ ""TINK"" , ""Konrad""], + ""description"": ""Südstadt Station"", + ""station"" : ""31"", + ""gps"" : { ""latitude"": ""47.69489"", ""longitude"": ""9.19"" } + } + }, + ""response_state"": ""OK"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BOOKING_REQUEST_SET02_001_FILE = @" + { + ""shareejson"" : { + ""response_state"" : ""OK: requested bike 8"", + ""bikes_occupied"" : { + ""87781"" : { + ""timeCode"" : ""3630"", + ""state"" : ""occupied"", + ""station"" : ""5"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-11-28 11:01:51.637747+01"", + ""bike"" : ""8"" + }, + ""87780"" : { + ""timeCode"" : ""1056"", + ""state"" : ""occupied"", + ""station"" : ""4"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-11-28 10:43:24.886288+01"", + ""bike"" : ""7"" + } + }, + ""timeCode"" : ""3630"", + ""response"" : ""booking_request"", + ""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51aoiF2kahH"", + ""apiserver"" : ""https://tinkwwp.copri-bike.de"" + } + } "; + + const string BIKES_OCCUPIED_REQUEST_SET01_001_FILE = @" + { + ""shareejson"" : { + ""apiserver"" : ""https://tinkwwp.copri-bike.de"", + ""bikes_occupied"" : { + ""87785"" : { + ""station"" : ""2"", + ""state"" : ""occupied"", + ""bike"" : ""20"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-12-01 22:21:57.740069+01"", + ""timeCode"" : ""6603"" + }, + ""87782"" : { + ""station"" : ""4"", + ""state"" : ""occupied"", + ""bike"" : ""7"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-11-28 13:06:55.147368+01"", + ""timeCode"" : ""2931"" + }, + ""87781"" : { + ""station"" : ""5"", + ""state"" : ""occupied"", + ""bike"" : ""8"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-11-28 11:01:51.637747+01"", + ""timeCode"" : ""3630"" + }, + }, + ""response_state"" : ""OK"", + ""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51a"", + ""response"" : ""user_bikes_occupied"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_OCCUPIED_REQUEST_SET02_001_FILE = @" + { + ""shareejson"": { + ""response_state"": ""OK"", + ""bikes_occupied"": { + ""87781"": { + ""timeCode"": ""3630"", + ""state"": ""occupied"", + ""station"" : ""5"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 11:01:51.637747+01"", + ""bike"": ""8"" + }, + ""87782"": { + ""timeCode"": ""2931"", + ""state"": ""occupied"", + ""station"" : ""4"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 13:06:55.147368+01"", + ""bike"": ""7"" + } + }, + ""authcookie"": ""4da3044c8657a04ba60e2eaa753bc51a"", + ""response"": ""user_bikes_occupied"", + ""apiserver"": ""https://tinkwwp.copri-bike.de"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_OCCUPIED_REQUEST_SET02_002_FILE = @" + { + ""shareejson"": { + ""response_state"": ""OK"", + ""bikes_occupied"": { + ""87781"": { + ""timeCode"": ""3630"", + ""state"": ""occupied"", + ""station"" : ""5"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 11:01:51.637747+01"", + ""bike"": ""8"" + }, + ""87782"": { + ""timeCode"": ""2931"", + ""state"": ""occupied"", + ""station"" : ""4"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 13:06:55.147368+01"", + ""bike"": ""7"" + }, + ""2360"": { + ""timeCode"": ""2360"", + ""state"": ""reserved"", + ""station"" : ""4"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 14:07:13.745568+01"", + ""bike"": ""5"" + } + }, + ""authcookie"": ""4da3044c8657a04ba60e2eaa753bc51a"", + ""response"": ""user_bikes_occupied"", + ""apiserver"": ""https://tinkwwp.copri-bike.de"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_OCCUPIED_REQUEST_SET02_003_FILE = @" + { + ""shareejson"": { + ""response_state"": ""OK"", + ""bikes_occupied"": { + ""87781"": { + ""timeCode"": ""3630"", + ""state"": ""occupied"", + ""station"" : ""5"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 11:01:51.637747+01"", + ""bike"": ""8"" + }, + ""87782"": { + ""timeCode"": ""2931"", + ""state"": ""occupied"", + ""station"" : ""4"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 13:06:55.147368+01"", + ""bike"": ""7"" + }, + ""2360"": { + ""timeCode"": ""2360"", + ""state"": ""occupied"", + ""station"" : ""4"", + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""start_time"": ""2017-11-28 14:08:32.756368+01"", + ""bike"": ""5"" + } + }, + ""authcookie"": ""4da3044c8657a04ba60e2eaa753bc51a"", + ""response"": ""user_bikes_occupied"", + ""apiserver"": ""https://tinkwwp.copri-bike.de"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_OCCUPIED_REQUEST_SHAREEFR01_SET1_FILE = @" + { + ""shareejson"": { + ""privacy_html"": ""site/privacy.html"", + ""response"": ""user_bikes_occupied"", + ""apiserver"": ""https://shareeapp-fr01.copri.eu"", + ""authcookie"": ""5781_d47fc786e740ef77d85a24bcb6f0ff97_oiF2kahH"", + ""impress_html"": ""site/impress.html"", + ""tariff_info_html"": ""site/tariff_info.html"", + ""bikes_occupied"": { + ""155776"": { + ""K_a"": ""[-83, 104, 55, -74, 16, -105, 124, -103, -68, -17, -127, -113, 56, 43, -61, -86, 124, -54, 63, 57, 0, 0, 0, 0]"", + ""real_hours"": ""49.3"", + ""bike"": ""1545"", + ""tariff_description"": { + ""eur_per_hour"" : ""2.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"", + ""max_eur_per_day"" : ""10.00"" + }, + ""lock_state"": ""unlocked"", + ""total_price"": ""23.75"", + ""station"" : ""103"", + ""gps"" : { ""latitude"": ""47.9980383301154"", ""longitude"": ""7.78496129438281"" }, + ""system"": ""Ilockit"", + ""end_time"": ""2020-11-08 19:11:27"", + ""bike_group"" : [ ""300029"" ], + ""Ilockit_ID"": ""ISHAREIT-2200545"", + ""start_time"": ""2020-11-06 17:53:22.784681+01"", + ""unit_price"": ""2.50"", + ""request_time"": ""2020-11-06 17:52:56.832138+01"", + ""state"": ""occupied"", + ""Ilockit_GUID"": ""00000000-0000-0000-0000-e38bf9d32234"", + ""computed_hours"": ""9.50"", + ""K_u"": ""[53, 92, -55, -109, 112, 38, -35, 43, -18, 122, 115, 50, -85, -57, 62, -57, -112, 2, 117, -47, 0, 0, 0, 0]"", + ""description"": ""Tester-bike (Ilockit)"", + ""K_seed"": ""[119, -47, -91, -36, -29, -17, 35, 47, -80, -112, -55, 2, 70, -45, 60, -62]"" + }, + ""155744"": { + ""Ilockit_GUID"": ""00000000-0000-0000-0000-d589a8023487"", + ""unit_price"": ""2.50"", + ""start_time"": ""2020-10-12 08:38:30.401679+02"", + ""request_time"": ""2020-10-12 08:38:12.374231+02"", + ""state"": ""occupied"", + ""K_u"": ""[93, -45, -89, -38, 50, 107, 21, -119, -26, -90, -15, 99, 17, 44, -20, 24, 34, -111, 125, -31, 0, 0, 0, 0]"", + ""computed_hours"": ""112"", + ""K_seed"": ""[24, 69, -21, 52, 55, 3, -71, 30, -71, 43, 108, -100, 15, 126, -117, 40]"", + ""description"": ""Oliver (Ilockit)"", + ""tariff_description"": { + ""eur_per_hour"" : ""2.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"", + ""max_eur_per_day"" : ""10.00"" + }, + ""bike"": ""1537"", + ""real_hours"": ""659.55"", + ""lock_state"": ""locked"", + ""K_a"": ""[109, 35, -55, 78, -65, 87, -43, -4, -90, -104, -16, -45, -80, -80, 106, -25, -77, 111, -79, 58, 0, 0, 0, 0]"", + ""system"": ""Ilockit"", + ""gps"" : { ""latitude"": ""47.9977741744369"", ""longitude"": ""7.78484562411904"" }, + ""station"" : ""103"", + ""total_price"": ""280.00"", + ""Ilockit_ID"": ""ISHAREIT-2200537"", + ""bike_group"" : [ ""300029"" ], + ""end_time"": ""2020-11-08 19:11:25"" + } + }, + ""bike_info_html"": ""site/bike_info.html"", + ""agb_html"": ""site/agb.html"", + ""agb_checked"": ""1"", + ""user_id"": ""ohauff@posteo.de"", + ""response_state"": ""OK: nothing todo "", + ""lang"": ""DE"", + ""user_group"": [ ""300029"", ""300001"" ], + ""debuglevel"": ""1"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_AVAILABLE_SET01_001_FILE = @" + { + ""shareejson"" : { + ""bikes"" : { + ""2352"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""1"", + ""gps"" : { ""latitude"": ""47.669888"", ""longitude"": ""9.167749"" }, + ""station"" : ""9"" + }, + ""2379"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""19"", + ""gps"" : { ""latitude"": ""47.6597846667"", ""longitude"": ""9.177439"" }, + ""station"" : ""3"" + }, + ""2354"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""2"", + ""gps"" : { ""latitude"": ""47.6721741667"", ""longitude"": ""9.176165"" }, + ""station"" : ""11"" + } + }, + ""response_state"" : ""OK"", + ""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51a"", + ""apiserver"" : ""https://tinkwwp.copri-bike.de"", + ""response"" : ""bikes_available"", + ""copri_version"" : ""4.1.0.0"" + } + } + "; + + const string BIKES_AVAILABLE_SET02_001_FILE = @" + { + ""shareejson"" : { + ""authcookie"" : 0, + ""apiserver"" : ""https://tinkwwp.copri-bike.de"", + ""response"" : ""bikes_available"", + ""bikes"" : { + ""3399"" : { + ""description"" : ""Cargo Trike"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""26"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.6586936667"", ""longitude"": ""9.16863116667"" }, + ""system"" : ""BC"", + ""station"" : ""4"" + }, + ""3398"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""25"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.669895"", ""longitude"": ""9.168173"" }, + ""system"" : ""BC"", + ""station"" : ""5"" + }, + ""2374"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""14"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""0.0"", ""longitude"": ""0.0"" }, + ""system"" : ""BC"", + ""station"" : ""4"" + }, + ""2373"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""13"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.6748671667"", ""longitude"": ""9.17326583333"" }, + ""system"" : ""BC"", + ""station"" : ""12"" + }, + ""2366"" : { + ""description"" : ""Cargo Trike"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""11"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.667938"", ""longitude"": ""9.164895"" }, + ""system"" : ""BC"", + ""station"" : ""5"" + }, + ""2379"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""19"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.666401"", ""longitude"": ""9.175902"" }, + ""system"" : ""BC"", + ""station"" : ""8"" + }, + ""2377"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""17"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.672231"", ""longitude"": ""9.175862"" }, + ""system"" : ""BC"", + ""station"" : ""11"" + }, + ""2378"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""18"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.665556"", ""longitude"": ""9.17095133333"" }, + ""system"" : ""BC"", + ""station"" : ""10"" + }, + ""2354"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""2"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.669895"", ""longitude"": ""9.168159"" }, + ""system"" : ""BC"", + ""station"" : ""5"" + }, + ""2360"" : { + ""description"" : ""Cargo long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""5"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.672082"", ""longitude"": ""9.17655283333"" }, + ""system"" : ""BC"", + ""station"" : ""4"" + }, + ""2380"" : { + ""description"" : ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"" : ""20"", + ""state"" : ""available"", + ""gps"" : { ""latitude"": ""47.6612413333"", ""longitude"": ""9.16641016667"" }, + ""system"" : ""BC"", + ""station"" : ""7"" + }, + ""52"" : { + ""state"" : ""available"", + ""bike"" : ""52"", + ""description"" : ""Stadtflitzer"", + ""bike_group"" : [ ""Konrad"" ], + ""station"" : ""31"", + ""system"" : ""BC"", + ""gps"" : { ""latitude"": ""47.67484"", ""longitude"": ""9.17254"" } + } + }, + ""response_state"" : ""OK"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_AVAILABLE_SET02_002_FILE = @" + { + ""shareejson"": { + ""authcookie"": 0, + ""apiserver"": ""https://tinkwwp.copri-bike.de"", + ""response"": ""bikes_available"", + ""bikes"": { + ""3399"": { + ""description"": ""Cargo Trike"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 26, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.6586936667"", ""longitude"": ""9.16863116667"" }, + ""station"" : ""4"" + }, + ""3398"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 25, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.669895"", ""longitude"": ""9.168173"" }, + ""station"" : ""5"" + }, + ""2374"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 14, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""0.0"", ""longitude"": ""0.0"" }, + ""station"" : ""4"" + }, + ""2373"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 13, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.6748671667"", ""longitude"": ""9.17326583333"" }, + ""station"" : ""12"" + }, + ""2366"": { + ""description"": ""Cargo Trike"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 11, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.667938"", ""longitude"": ""9.164895"" }, + ""station"" : ""5"" + }, + ""2379"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 19, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.666401"", ""longitude"": ""9.175902"" }, + ""station"" : ""8"" + }, + ""2377"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 17, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.672231"", ""longitude"": ""9.175862"" }, + ""station"" : ""11"" + }, + ""2378"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 18, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.665556"", ""longitude"": ""9.17095133333"" }, + ""station"" : ""10"" + }, + ""2354"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 2, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.669895"", ""longitude"": ""9.168159"" }, + ""station"" : ""5"" + }, + ""2380"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 20, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.6612413333"", ""longitude"": ""9.16641016667"" }, + ""station"" : ""7"" + }, + ""52"" : { + ""state"" : ""available"", + ""bike"" : ""52"", + ""description"" : ""Stadtflitzer"", + ""bike_group"" : [ ""Konrad"" ], + ""station"" : ""5"", + ""gps"" : { ""latitude"": ""47.67484"", ""longitude"": ""9.17254"" } + } + }, + ""response_state"": ""OK"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_AVAILABLE_SET02_003_FILE = @" + { + ""shareejson"": { + ""authcookie"": 0, + ""apiserver"": ""https://tinkwwp.copri-bike.de"", + ""response"": ""bikes_available"", + ""bikes"": { + ""3399"": { + ""description"": ""Cargo Trike"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 26, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.6586936667"", ""longitude"": ""9.16863116667"" }, + ""station"" : ""4"" + }, + ""3398"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 25, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.669895"", ""longitude"": ""9.168173"" }, + ""station"" : ""5"" + }, + ""2374"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 14, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""0.0"", ""longitude"": ""0.0"" }, + ""station"" : ""4"" + }, + ""2373"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 13, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.6748671667"", ""longitude"": ""9.17326583333"" }, + ""station"" : ""12"" + }, + ""2366"": { + ""description"": ""Cargo Trike"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 11, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.667938"", ""longitude"": ""9.164895"" }, + ""station"" : ""5"" + }, + ""2379"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 19, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.666401"", ""longitude"": ""9.175902"" }, + ""station"" : ""8"" + }, + ""2377"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 17, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.672231"", ""longitude"": ""9.175862"" }, + ""station"" : ""11"" + }, + ""2378"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 18, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.665556"", ""longitude"": ""9.17095133333"" }, + ""station"" : ""10"" + }, + ""2354"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 2, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.669895"", ""longitude"": ""9.168159"" }, + ""station"" : ""5"" + }, + ""2380"": { + ""description"": ""Cargo Long"", + ""bike_group"" : [ ""TINK"" ], + ""bike"": 20, + ""state"": ""available"", + ""gps"" : { ""latitude"": ""47.6612413333"", ""longitude"": ""9.16641016667"" }, + ""station"" : ""7"" + }, + ""52"" : { + ""state"" : ""available"", + ""bike"" : ""52"", + ""description"" : ""Stadtflitzer"", + ""bike_group"" : [ ""Konrad"" ], + ""station"" : ""5"", + ""gps"" : { ""latitude"": ""47.67484"", ""longitude"": ""9.17254"" } + } + }, + ""response_state"": ""OK"", + ""copri_version"" : ""4.1.0.0"" + } + }"; + + const string BIKES_AVAILABLE_REQUEST_SHAREEFR01_SET1_FILE = @" + { + ""shareejson"": { + ""user_id"": ""ohauff@posteo.de"", + ""privacy_html"": ""site/privacy.html"", + ""agb_checked"": ""1"", + ""debuglevel"": ""1"", + ""agb_html"": ""site/agb.html"", + ""bike_info_html"": ""site/bike_info.html"", + ""copri_version"": ""4.1.0.0"", + ""apiserver"": ""https://shareeapp-fr01.copri.eu"", + ""lang"": ""DE"", + ""impress_html"": ""site/impress.html"", + ""response"": ""bikes_available"", + ""tariff_info_html"": ""site/tariff_info.html"", + ""user_group"": [ ""300029"", ""300001"" ], + ""authcookie"": ""5781_d47fc786e740ef77d85a24bcb6f0ff97_oiF2kahH"", + ""response_state"": ""OK: nothing todo "", + ""bikes"": { + ""1536"": { + ""bike"": ""1536"", + ""tariff_description"": { + ""eur_per_hour"" : ""3.00"", + ""number"" : ""5491"", + ""name"" : ""Vauban Basic"", + ""max_eur_per_day"" : ""15.00"" + }, + ""description"": ""Vauban-bike (Ilockit)"", + ""state"": ""available"", + ""station"" : ""101"", + ""gps"" : { ""latitude"": ""47.976741"", ""longitude"": ""7.8257615"" }, + ""lock_state"": ""locked"", + ""Ilockit_ID"": ""ISHAREIT-2200536"", + ""Ilockit_GUID"": ""00000000-0000-0000-0000-caa87760e53e"", + ""system"": ""Ilockit"", + ""bike_group"" : [ ""300001"" ] + }, + ""1539"": { + ""gps"" : { ""latitude"": ""47.992718"", ""longitude"": ""7.833071"" }, + ""station"" : ""104"", + ""tariff_description"": { + ""eur_per_hour"" : ""3.00"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"", + ""max_eur_per_day"" : ""10.00"" + }, + ""description"": ""Tester-bike (Ilockit)"", + ""bike"": ""1539"", + ""state"": ""available"", + ""bike_group"" : [ ""300029"" ], + ""system"": ""Ilockit"", + ""lock_state"": ""unlocked"", + ""Ilockit_ID"": ""ISHAREIT-2200539"", + ""Ilockit_GUID"": ""00000000-0000-0000-0000-f0b4a692e169"" + }, + ""1315"": { + ""system"": ""Ilockit"", + ""bike_group"" : [ ""300029"" ], + ""Ilockit_GUID"": ""00000000-0000-0000-e57e6b9aee16"", + ""Ilockit_ID"": ""ISHAREIT-2200315"", + ""lock_state"": ""locked"", + ""gps"" : { ""latitude"": ""47.997930"", ""longitude"": ""7.785428"" }, + ""station"" : ""103"", + ""state"": ""available"", + ""bike"": ""1315"", + ""description"": ""Tester-bike (Ilockit)"", + ""tariff_description"": { + ""eur_per_hour"" : ""3.00"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"", + ""max_eur_per_day"" : ""10.00"" + }, + }, + ""1544"": { + ""station"" : ""101"", + ""gps"" : { ""latitude"": ""47.976634"", ""longitude"": ""7.825490"" }, + ""state"": ""available"", + ""tariff_description"": { + ""eur_per_hour"" : ""3.00"", + ""number"" : ""5491"", + ""name"" : ""Vauban Basic"", + ""max_eur_per_day"" : ""15.00"" + }, + ""description"": ""Vauban-bike (Ilockit)"", + ""bike"": ""1544"", + ""bike_group"" : [ ""300001"" ], + ""system"": ""Ilockit"", + ""Ilockit_GUID"": ""00000000-0000-0000-0000-dc969f648732"", + ""Ilockit_ID"": ""ISHAREIT-2200544"", + ""lock_state"": ""locked"" + }, + ""1543"": { + ""gps"" : { ""latitude"": ""47.9979824"", ""longitude"": ""7.7848131"" }, + ""station"" : ""103"", + ""bike"": ""1543"", + ""description"": ""Tester-bike (Ilockit)"", + ""tariff_description"": { + ""eur_per_hour"" : ""3.00"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"", + ""max_eur_per_day"" : ""10.00"" + }, + ""state"": ""available"", + ""system"": ""Ilockit"", + ""bike_group"" : [ ""300029"" ], + ""Ilockit_ID"": ""ISHAREIT-2200543"", + ""lock_state"": ""locked"", + ""Ilockit_GUID"": ""00000000-0000-0000-0000-cc141a6f68bb"" + } + } + } + }"; + + private const SampleSets DEFAULT_SAMPLE_SET = SampleSets.Set2; + + private const int DEFAULT_STAGE_INDEX = 1; + + private IRequestBuilder requestBuilder; + + public CopriCallsMemory( + SampleSets? sampleSet = null, + int? index = null, + string sessionCookie = null) + { + ActiveSampleSet = sampleSet ?? DEFAULT_SAMPLE_SET; + ActiveStageIndex = index ?? DEFAULT_STAGE_INDEX; + SessionCookie = sessionCookie; + + requestBuilder = string.IsNullOrEmpty(sessionCookie) + ? new RequestBuilder(MerchantId) as IRequestBuilder + : new RequestBuilderLoggedIn(MerchantId, sessionCookie); + + } + + /// Holds the session id of the logged in user, null otherwise. + public string SessionCookie { get; private set; } + + /// Logs user in. + /// User to log in. + /// Id specifying user and hardware. + /// Mailaddress of user to log in. + /// Password to log in. + /// Response which holds auth cookie + public async Task DoAuthorizationAsync( + string p_strMailAddress, + string p_strPassword, + string p_strDeviceId) + { + return await Task.Run(() => DoAuthorize(p_strMailAddress, p_strPassword, p_strDeviceId)); + } + + /// Logs user out. + /// User to log in. + /// Response which holds auth cookie + public async Task DoAuthoutAsync() + { + return await Task.Run(() => DoAuthout(SessionCookie)); + } + + /// + /// Gets list of bikes from memory. + /// + /// + public async Task GetBikesAvailableAsync() + { + return await Task.Run(() => GetBikesAvailable(null, SessionCookie, ActiveSampleSet, ActiveStageIndex)); + } + + /// + /// Gets a list of bikes reserved/ booked by acctive user from Copri. + /// + /// Cookie to authenticate user. + /// Response holding list of bikes. + public async Task GetBikesOccupiedAsync() + { + try + { + requestBuilder.GetBikesOccupied(); // Non mock implementation if ICopriServer call this member as well. To ensure comparable behaviour this member is called here as well. + } + catch (NotSupportedException) + { + // No user logged in. + await Task.CompletedTask; + return ResponseHelper.GetBikesOccupiedNone(); + } + return await Task.Run(() => GetBikesOccupied(SessionCookie, ActiveSampleSet, ActiveStageIndex)); + } + + /// + /// Get list of stations from file. + /// + /// Auto cookie of user if user is logged in. + /// List of files. + public async Task GetStationsAsync() + { + return await Task.Run(() => GetStationsAll(null, SessionCookie, ActiveSampleSet, ActiveStageIndex)); + } + + /// + /// Gets booking request response. + /// + /// Id of the bike to book. + /// Booking response. + public async Task DoReserveAsync(string bikeId, Uri operatorUri) + { + return await Task.Run(() => DoReserve(bikeId, SessionCookie, ActiveSampleSet, ActiveStageIndex)); + } + + /// + /// Gets canel booking request response. + /// + /// Id of the bike to book. + /// Cookie of the logged in user. + /// Response on cancel booking request. + public async Task DoCancelReservationAsync(string bikeId, Uri operatorUri) + { + return await Task.Run(() => DoCancelReservation(bikeId, SessionCookie, ActiveSampleSet, ActiveStageIndex)); + } + + /// + /// Gets the count of stages that exist for a given sample set. + /// + /// + /// + public int StagesCount + { + get + { + var l_iCount = 1; + while (GetBikesAvailable(CopriDevelHostUri, TinkApp.MerchantId, p_eSampleSet: ActiveSampleSet, p_lStageIndex: l_iCount) != null) + { + l_iCount++; + } + + return l_iCount - 1; + } + } + + /// Gets the merchant id. + public string MerchantId => TinkApp.MerchantId; + + /// + /// Gets the active sample set. + /// + public SampleSets ActiveSampleSet { get; } + + /// + /// Gets the active stage index. + /// + public int ActiveStageIndex { get; } + + /// Returns false because cached values are returned. + public bool IsConnected => false; + + /// Logs user in. + /// User to log in. + /// Id specifying user and hardware. + /// Mailaddress of user to log in. + /// Password to log in. + /// Response which holds auth cookie + public static AuthorizationResponse DoAuthorize( + string p_strMailAddress, + string p_strPassword, + string p_strDeviceId, + SampleSets p_eSampleSet = DEFAULT_SAMPLE_SET, + long p_lStageIndex = DEFAULT_STAGE_INDEX) + { + switch (p_eSampleSet) + { + case SampleSets.Set2: + switch (p_lStageIndex) + { + case 1: + // Response contains auth cookie of user "JavaministerHardwareNr1" + // For this reason do not return answer if mail and pwd do not match. + + return p_strMailAddress == "javaminister@gmail.com" + && p_strPassword == "javaminister" && + p_strDeviceId == "HwId1000000000000" + ? JsonConvertRethrow.DeserializeObject>(DO_AUTH_SET02_001_FILE).shareejson + : JsonConvertRethrow.DeserializeObject>(DO_AUTH_Unknown_User_FILE).shareejson; + + default: + return null; + } + + default: + return null; + } + } + + /// Logs user in. + /// Response which holds auth cookie + public static AuthorizationoutResponse DoAuthout( + string sessionCookie, + SampleSets p_eSampleSet = DEFAULT_SAMPLE_SET, + long p_lStageIndex = DEFAULT_STAGE_INDEX) + { + switch (p_eSampleSet) + { + case SampleSets.Set2: + switch (p_lStageIndex) + { + case 1: + // Response contains auth cookie of user "JavaministerHardwareNr1" + // For this reason do not return answer if mail and pwd do not match. + + return !string.IsNullOrEmpty(sessionCookie) + ? JsonConvertRethrow.DeserializeObject>(DO_AUTHOUT_SET02_001_FILE).shareejson + : throw new NotSupportedException(); + + default: + return null; + } + + default: + return null; + } + } + + /// + /// Gets list of bikes from memory. + /// + /// Id of the merchant. + /// Auto cookie of user if user is logged in. + /// Set of samples. + /// Index of the stage. + /// + public static BikesAvailableResponse GetBikesAvailable( + string p_strMerchantId, + string p_strSessionCookie = null, + SampleSets p_eSampleSet = DEFAULT_SAMPLE_SET, + long p_lStageIndex = DEFAULT_STAGE_INDEX) + { + switch (p_eSampleSet) + { + case SampleSets.Set1: + switch (p_lStageIndex) + { + case 1: + return CopriCallsStatic.DeserializeResponse(BIKES_AVAILABLE_SET01_001_FILE); + + default: + return null; + } + case SampleSets.Set2: + switch (p_lStageIndex) + { + case 1: + return CopriCallsStatic.DeserializeResponse(BIKES_AVAILABLE_SET02_001_FILE); + + case 2: + return CopriCallsStatic.DeserializeResponse(BIKES_AVAILABLE_SET02_002_FILE); + + case 3: + return CopriCallsStatic.DeserializeResponse(BIKES_AVAILABLE_SET02_003_FILE); + + default: + return null; + } + + case SampleSets.ShareeFr01_Set1: + switch (p_lStageIndex) + { + case 1: + return CopriCallsStatic.DeserializeResponse(BIKES_AVAILABLE_REQUEST_SHAREEFR01_SET1_FILE); + + default: + return null; + } + + default: + return null; + } + } + + /// + /// Gets stations response. + /// + /// Id of the merchant. + /// Auto cookie of user if user is logged in. + /// + /// + /// + public static StationsAllResponse GetStationsAll( + string p_strMerchantId, + string p_strCookie = null, + SampleSets p_eSampleSet = DEFAULT_SAMPLE_SET, + long p_lStageIndex = DEFAULT_STAGE_INDEX) + { + switch (p_eSampleSet) + { + case SampleSets.Set2: + switch (p_lStageIndex) + { + case 1: + return JsonConvertRethrow.DeserializeObject>(STATIONS_SET02_001_FILE).shareejson; + + default: + return null; + + } + + default: + return null; + } + } + + /// + /// Gets booking request response. + /// + /// Id of the bike. + /// Identifies the logged in user. + /// Sample set to use. + /// Index of the stage. + /// + public static ReservationBookingResponse DoReserve( + string bikeId, + string sessionCookie, + SampleSets sampleSet = DEFAULT_SAMPLE_SET, + long stageIndex = DEFAULT_STAGE_INDEX) + { + ReservationBookingResponse l_oResponse; + switch (sampleSet) + { + case SampleSets.Set2: + switch (stageIndex) + { + case 1: + l_oResponse = JsonConvertRethrow.DeserializeObject>(BOOKING_REQUEST_SET02_001_FILE).shareejson; + break; + + default: + return null; + + } + break; + + default: + return null; + } + + return l_oResponse; + } + + /// + /// Gets canel booking request response. + /// + /// Id of the bike to book. + /// Cookie of the logged in user. + /// Response on cancel booking request. + public static ReservationCancelReturnResponse DoCancelReservation( + string bikeId, + string cookie, + SampleSets sampleSet = DEFAULT_SAMPLE_SET, + long stageIndex = DEFAULT_STAGE_INDEX) + { + return null; + } + + public Task CalculateAuthKeysAsync(string bikeId, Uri operatorUri) => null; + + public Task UpdateLockingStateAsync( + string bikeId, + LocationDto geolocation, + lock_state state, + double batteryLevel, + Uri operatorUri) => null; + + public Task DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri) + { + return null; + } + + public Task DoReturn( + string bikeId, + LocationDto geolocation, + ISmartDevice smartDevice, + Uri operatorUri) + { + return null; + } + + public Task DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri operatorUri) => null; + + /// + /// Gets a list of bikes reserved/ booked by acctive user from Copri. + /// + /// Cookie to authenticate user. + /// Sample set to use. + /// Index of the stage. + /// Response holding list of bikes. + public static BikesReservedOccupiedResponse GetBikesOccupied( + string p_strSessionCookie = null, + SampleSets sampleSet = DEFAULT_SAMPLE_SET, + long stageIndex = DEFAULT_STAGE_INDEX) + { + BikesReservedOccupiedResponse response; + switch (sampleSet) + { + case SampleSets.Set1: + + switch (stageIndex) + { + case 1: + response = CopriCallsStatic.DeserializeResponse(BIKES_OCCUPIED_REQUEST_SET01_001_FILE); + break; + + default: + return null; + } + + break; + + case SampleSets.Set2: + switch (stageIndex) + { + case 1: + response = CopriCallsStatic.DeserializeResponse(BIKES_OCCUPIED_REQUEST_SET02_001_FILE); + break; + + case 2: + response = CopriCallsStatic.DeserializeResponse(BIKES_OCCUPIED_REQUEST_SET02_002_FILE); + break; + + case 3: + response = CopriCallsStatic.DeserializeResponse(BIKES_OCCUPIED_REQUEST_SET02_003_FILE); + break; + + default: + return null; + } + break; + + case SampleSets.ShareeFr01_Set1: + switch (stageIndex) + { + case 1: + response = CopriCallsStatic.DeserializeResponse(BIKES_OCCUPIED_REQUEST_SHAREEFR01_SET1_FILE); + break; + + + default: + return null; + } + + + break; + + + default: + return null; + } + + return p_strSessionCookie == null || response?.authcookie == p_strSessionCookie + ? response + : ResponseHelper.GetBikesOccupiedNone(p_strSessionCookie); + + } + + public enum SampleSets + { + /// Used for tests and demo GUI. + Set1, + + /// + /// Sample set nr. 2. + /// Bikes values from first Copri- JSON returns these values. + /// + Set2, + + /// + /// Sharee server + /// + ShareeFr01_Set1, + } + } +} diff --git a/TINKLib/Repository/CopriCallsMonkeyStore.cs b/TINKLib/Repository/CopriCallsMonkeyStore.cs index 9bf4d1f..5750d93 100644 --- a/TINKLib/Repository/CopriCallsMonkeyStore.cs +++ b/TINKLib/Repository/CopriCallsMonkeyStore.cs @@ -1,13 +1,12 @@ using MonkeyCache.FileStore; using System; using System.Threading.Tasks; -using TINK.Model.Connector; -using TINK.Model.Repository.Request; -using TINK.Model.Repository.Response; -using TINK.Model.Services.CopriApi; +using TINK.Repository.Request; using TINK.Repository.Response; +using TINK.Model.Services.CopriApi; +using TINK.Model.Device; -namespace TINK.Model.Repository +namespace TINK.Repository { public class CopriCallsMonkeyStore : ICopriCache { @@ -18,7 +17,7 @@ namespace TINK.Model.Repository private IRequestBuilder requestBuilder; public const string BIKESAVAILABLE = @"{ - ""copri_version"" : ""3.0.0.0"", + ""copri_version"" : ""4.1.0.0"", ""bikes"" : {}, ""response_state"" : ""OK"", ""apiserver"" : ""https://app.tink-konstanz.de"", @@ -26,26 +25,78 @@ namespace TINK.Model.Repository ""response"" : ""bikes_available"" }"; + /// Gets an empty response. + /// Version of empty response. + /// Response. + public static BikesAvailableResponse GetEmptyBikesAvailableResponse(string copriVersion) + => JsonConvertRethrow.DeserializeObject(BIKESAVAILABLE.Replace("4.1.0.0", copriVersion)); + +#if !COPRIVERSION41 public const string BIKESOCCUPIED = @"{ ""debuglevel"" : ""1"", ""user_id"" : """", ""response"" : ""user_bikes_occupied"", - ""user_group"" : ""Konrad,TINK"", + ""user_group"" : [ ""Konrad"", ""TINK"" ], ""authcookie"" : """", ""response_state"" : ""OK"", ""bikes_occupied"" : {}, - ""copri_version"" : ""3.0.0.0"", + ""copri_version"" : ""4.1.0.0"", ""apiserver"" : ""https://app.tink-konstanz.de"" }"; +#endif - public const string STATIONS = @"{ - ""apiserver"" : ""https://app.tink-konstanz.de"", + /// Gets an empty response. + /// Version of empty response. + /// Response. + public static BikesReservedOccupiedResponse GetEmptyBikesReservedOccupiedResponse(string copriVersion) + => JsonConvertRethrow.DeserializeObject(BIKESOCCUPIED.Replace("4.1.0.0", copriVersion)); + +#if !COPRIVERSION41 + /// Version COPRI 4.0. or earlier + public const string STATIONSALL = @"{ + ""apiserver"" : """", ""authcookie"" : """", ""response"" : ""stations_all"", - ""copri_version"" : ""3.0.0.0"", + ""copri_version"" : ""4.1.0.0"", ""stations"" : {}, ""response_state"" : ""OK"" }"; +#else + /// Version COPRI 4.1 or later. + public const string STATIONSALL = @"{ + ""uri_primary"": """" + ""uri_operator_array"": [ ] + ""authcookie"" : """", + ""response"" : ""stations_all"", + ""copri_version"" : ""4.1.0.0"", + ""stations"" : {}, + ""response_state"" : ""OK"" + }"; +#endif + +#if !COPRIVERSION41 + /// Version COPRI 4.0. or earlier + public const string AUTHORIZATION = @"{ + ""user_group"" : [ """" ], + ""response"" : ""authorization"", + ""response_state"" : ""OK"", + ""copri_version"" : ""4.1.0.0"" + }"; +#else + /// Version COPRI 4.0. or earlier + public const string AUTHORIZATION = @"{ + ""user_group"" : [], + ""response"" : ""authorization"", + ""response_state"" : ""OK"", + ""copri_version"" : ""4.1.0.0"" + }"; +#endif + + /// Gets an empty response. + /// Version of empty response. + /// Response. + public static StationsAllResponse GetEmptyStationsAllResponse(string copriVersion) + => JsonConvertRethrow.DeserializeObject(STATIONSALL.Replace("4.1.0.0", copriVersion)); /// /// Holds the seconds after which station and bikes info is considered to be invalid. @@ -79,53 +130,57 @@ namespace TINK.Model.Repository // Ensure that store holds valid entries. if (!Barrel.Current.Exists(requestBuilder.GetBikesAvailable())) { - AddToCache(JsonConvert.DeserializeObject(BIKESAVAILABLE), new TimeSpan(0)); + AddToCache(JsonConvertRethrow.DeserializeObject(BIKESAVAILABLE), new TimeSpan(0)); } // Do not query bikes occupied if no user is logged in (leads to not implemented exception) if (!string.IsNullOrEmpty(sessionCookie) && !Barrel.Current.Exists(requestBuilder.GetBikesOccupied())) { - AddToCache(JsonConvert.DeserializeObject(BIKESOCCUPIED), new TimeSpan(0)); + AddToCache(JsonConvertRethrow.DeserializeObject(BIKESOCCUPIED), new TimeSpan(0)); } if (!Barrel.Current.Exists(requestBuilder.GetStations())) { - AddToCache(JsonConvert.DeserializeObject(STATIONS), new TimeSpan(0)); + AddToCache(JsonConvertRethrow.DeserializeObject(STATIONSALL), new TimeSpan(0)); } } - public Task DoReserveAsync(int bikeId, Uri operatorUri) + public Task DoReserveAsync(string bikeId, Uri operatorUri) { throw new System.Exception("Reservierung im Offlinemodus nicht möglich!"); } - public Task DoCancelReservationAsync(int p_iBikeId, Uri operatorUri) + public Task DoCancelReservationAsync(string bikeId, Uri operatorUri) { throw new System.Exception("Abbrechen einer Reservierung im Offlinemodus nicht möglich!"); } - public Task CalculateAuthKeysAsync(int bikeId, Uri operatorUri) + public Task CalculateAuthKeysAsync(string bikeId, Uri operatorUri) => throw new System.Exception("Schlosssuche im Offlinemodus nicht möglich!"); public Task UpdateLockingStateAsync( - int bikeId, + string bikeId, LocationDto geolocation, lock_state state, double batteryLevel, Uri operatorUri) => throw new System.Exception("Aktualisierung des Schlossstatuses im Offlinemodus nicht möglich!"); - public Task DoBookAsync(int bikeId, Guid guid, double batteryPercentage, Uri operatorUri) + public Task DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri) { throw new System.Exception("Buchung im Offlinemodus nicht möglich!"); } - public Task DoReturn(int bikeId, LocationDto geolocation, Uri operatorUri) + public Task DoReturn( + string bikeId, + LocationDto geolocation, + ISmartDevice smartDevice, + Uri operatorUri) { throw new System.Exception("Rückgabe im Offlinemodus nicht möglich!"); } - public Task DoSubmitFeedback(string message, bool isBikeBroken, Uri operatorUri) => + public Task DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri operatorUri) => throw new System.Exception("Übermittlung von Feedback im Offlinemodus nicht möglich!"); public Task DoAuthorizationAsync(string p_strMailAddress, string p_strPassword, string p_strDeviceId) @@ -207,7 +262,7 @@ namespace TINK.Model.Repository { Barrel.Current.Add( requestBuilder.GetStations(), - JsonConvert.SerializeObject(stations), + JsonConvertRethrow.SerializeObject(stations), expiresAfter); } } @@ -239,8 +294,8 @@ namespace TINK.Model.Repository lock (monkeyLock) { Barrel.Current.Add( - requestBuilder.GetBikesAvailable(), - JsonConvert.SerializeObject(bikes), + requestBuilder.GetBikesAvailable(), + JsonConvertRethrow.SerializeObject(bikes), expiresAfter); } } @@ -273,7 +328,7 @@ namespace TINK.Model.Repository { Barrel.Current.Add( requestBuilder.GetBikesOccupied(), - JsonConvert.SerializeObject(bikes), + JsonConvertRethrow.SerializeObject(bikes), expiresAfter); } } diff --git a/TINKLib/Repository/CopriCallsStatic.cs b/TINKLib/Repository/CopriCallsStatic.cs index 7fef62f..e84d0a5 100644 --- a/TINKLib/Repository/CopriCallsStatic.cs +++ b/TINKLib/Repository/CopriCallsStatic.cs @@ -1,28 +1,65 @@ -using TINK.Model.Repository.Response; +using Serilog; +using System; +using TINK.Model.Connector; using TINK.Repository.Response; -namespace TINK.Model.Repository +namespace TINK.Repository { public static class CopriCallsStatic { - /// - /// Deserializes JSON from response string. - /// - /// Response to deserialize. - /// - public static BikesAvailableResponse DeserializeBikesAvailableResponse(string p_strResponse) +#if !USCSHARP9 + private static Version UNSUPPORTEDFUTURECOPRIVERSIONLOWER = new Version(4, 1); +#else + private static Version UNSUPPORTEDFUTURECOPRIVERSIONLOWER = new(4, 1); +#endif + +#if !USCSHARP9 + private static Version UNSUPPORTEDFUTURECOPRIVERSIONUPPER = new Version(4, 2); +#else + private static Version UNSUPPORTEDFUTURECOPRIVERSIONUPPER = new(4, 2); +#endif + public static Version UnsupportedVersionLower => UNSUPPORTEDFUTURECOPRIVERSIONLOWER; + + public static Version UnsupportedVersionUpper => UNSUPPORTEDFUTURECOPRIVERSIONUPPER; + + /// Deserializes reponse JSON if response is of supported version or provides default response otherwise. + /// Type of response object. + /// Response JSON. + /// Factory providing default delegate. + /// Response object. + public static T DeserializeResponse(this string response, Func emptyResponseFactory) where T: class { - return JsonConvert.DeserializeObject>(p_strResponse)?.tinkjson; + // Get COPRI version from respone. + var bikeInfoBase = JsonConvertRethrow.DeserializeObject(response)?.shareejson; + + if (bikeInfoBase.GetCopriVersion() < UNSUPPORTEDFUTURECOPRIVERSIONLOWER + || bikeInfoBase.GetCopriVersion() >= UNSUPPORTEDFUTURECOPRIVERSIONUPPER) + { + return emptyResponseFactory?.Invoke(bikeInfoBase.copri_version) ?? null; + } + + return JsonConvertRethrow.DeserializeObject>(response)?.shareejson; } - /// - /// Deserializes JSON from response string. - /// - /// Response to deserialize. - /// - public static BikesReservedOccupiedResponse DeserializeBikesOccupiedResponse(string p_strResponse) + /// Deserializes reponse JSON if response is of supported version or throws an exception. + /// Type of response object. + /// Response JSON. + /// Exception to fire. + /// Response object. + public static T DeserializeResponse(this string response, Func unsupportedVersionExectpion = null) where T : class { - return JsonConvert.DeserializeObject>(p_strResponse)?.tinkjson; + + // Get COPRI version from respone. + var bikeInfoBase = JsonConvertRethrow.DeserializeObject(response)?.shareejson; + + if (bikeInfoBase.GetCopriVersion() < UNSUPPORTEDFUTURECOPRIVERSIONLOWER + || bikeInfoBase.GetCopriVersion() >= UNSUPPORTEDFUTURECOPRIVERSIONUPPER) + { + Log.Error($"Unsupported copri version {bikeInfoBase.copri_version} detected on attempt to log in."); + throw unsupportedVersionExectpion?.Invoke(bikeInfoBase.copri_version) ?? new System.Exception($"Unsupported COPRI version {bikeInfoBase.copri_version} detected."); + } + + return JsonConvertRethrow.DeserializeObject>(response)?.shareejson; } } } diff --git a/TINKLib/Repository/Exception/AuthcookieNotDefinedException.cs b/TINKLib/Repository/Exception/AuthcookieNotDefinedException.cs index 9aa5b0e..283a58f 100644 --- a/TINKLib/Repository/Exception/AuthcookieNotDefinedException.cs +++ b/TINKLib/Repository/Exception/AuthcookieNotDefinedException.cs @@ -1,4 +1,4 @@ -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { /// /// Is fired with reqest used a cookie which is not defined. @@ -15,11 +15,25 @@ { } + /// + /// Gets whether authcookie is defined or not. + /// + /// Response to check + /// Text holding contectin in which authcookie is checked. + /// Exception thrown if cookie is not defined. + /// public static bool IsAuthcookieNotDefined( Response.ResponseBase reponse, string actionText, out AuthcookieNotDefinedException exception) { + if (reponse == null || reponse.response_state == null) + { + // Empty response or response withoud response state is no authcookie not defined exeception. + exception = null; + return false; + } + if (!reponse.response_state.ToUpper().Contains(AUTH_FAILURE_QUERY_AUTHCOOKIENOTDEFIED.ToUpper()) && !reponse.response_state.ToUpper().Contains(AUTH_FAILURE_BOOK_AUTICOOKIENOTDEFIED.ToUpper()) && !reponse.response_state.ToUpper().Contains(AUTH_FAILURE_BIKESOCCUPIED_AUTICOOKIENOTDEFIED.ToUpper()) diff --git a/TINKLib/Repository/Exception/AuthorizationResponseException.cs b/TINKLib/Repository/Exception/AuthorizationResponseException.cs index aec2163..7449666 100644 --- a/TINKLib/Repository/Exception/AuthorizationResponseException.cs +++ b/TINKLib/Repository/Exception/AuthorizationResponseException.cs @@ -1,4 +1,4 @@ -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public class InvalidAuthorizationResponseException : InvalidResponseException { diff --git a/TINKLib/Repository/Exception/BookingDeclinedException.cs b/TINKLib/Repository/Exception/BookingDeclinedException.cs index 98982aa..333f3b0 100644 --- a/TINKLib/Repository/Exception/BookingDeclinedException.cs +++ b/TINKLib/Repository/Exception/BookingDeclinedException.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { /// Handles booking request which fail due to too many bikes requested/ booked. public class BookingDeclinedException : InvalidResponseException diff --git a/TINKLib/Repository/Exception/CallNotRequiredException.cs b/TINKLib/Repository/Exception/CallNotRequiredException.cs index ccaa218..3ecef61 100644 --- a/TINKLib/Repository/Exception/CallNotRequiredException.cs +++ b/TINKLib/Repository/Exception/CallNotRequiredException.cs @@ -1,4 +1,4 @@ -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public class CallNotRequiredException : System.Exception { diff --git a/TINKLib/Repository/Exception/CommunicationException.cs b/TINKLib/Repository/Exception/CommunicationException.cs index fcc645a..77ef542 100644 --- a/TINKLib/Repository/Exception/CommunicationException.cs +++ b/TINKLib/Repository/Exception/CommunicationException.cs @@ -1,4 +1,4 @@ -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public class CommunicationException : System.Exception { diff --git a/TINKLib/Repository/Exception/DeserializationException.cs b/TINKLib/Repository/Exception/DeserializationException.cs index c6f9c49..658256b 100644 --- a/TINKLib/Repository/Exception/DeserializationException.cs +++ b/TINKLib/Repository/Exception/DeserializationException.cs @@ -1,6 +1,4 @@ -using TINK.Model.Repository.Exception; - -namespace TINK.Repository.Exception +namespace TINK.Repository.Exception { public class DeserializationException : CommunicationException { diff --git a/TINKLib/Repository/Exception/InvalidResponseException.cs b/TINKLib/Repository/Exception/InvalidResponseException.cs index b129c78..cae3b97 100644 --- a/TINKLib/Repository/Exception/InvalidResponseException.cs +++ b/TINKLib/Repository/Exception/InvalidResponseException.cs @@ -1,4 +1,4 @@ -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public class InvalidResponseException : InvalidResponseException { diff --git a/TINKLib/Repository/Exception/NoGPSDataException.cs b/TINKLib/Repository/Exception/NoGPSDataException.cs index 042fca8..5e44a29 100644 --- a/TINKLib/Repository/Exception/NoGPSDataException.cs +++ b/TINKLib/Repository/Exception/NoGPSDataException.cs @@ -1,5 +1,4 @@ -using TINK.Model.Repository.Exception; - + namespace TINK.Repository.Exception { public class NoGPSDataException : InvalidResponseException diff --git a/TINKLib/Repository/Exception/NotAtStationException.cs b/TINKLib/Repository/Exception/NotAtStationException.cs index f0b12c3..5157727 100644 --- a/TINKLib/Repository/Exception/NotAtStationException.cs +++ b/TINKLib/Repository/Exception/NotAtStationException.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public class NotAtStationException : InvalidResponseException { diff --git a/TINKLib/Repository/Exception/ResponseException.cs b/TINKLib/Repository/Exception/ResponseException.cs index a4e3122..1c00ac8 100644 --- a/TINKLib/Repository/Exception/ResponseException.cs +++ b/TINKLib/Repository/Exception/ResponseException.cs @@ -1,4 +1,4 @@ -using TINK.Model.Repository.Response; +using TINK.Repository.Response; namespace TINK.Repository.Exception { diff --git a/TINKLib/Repository/Exception/ReturnBikeException.cs b/TINKLib/Repository/Exception/ReturnBikeException.cs index 31bc119..7d2d08d 100644 --- a/TINKLib/Repository/Exception/ReturnBikeException.cs +++ b/TINKLib/Repository/Exception/ReturnBikeException.cs @@ -1,4 +1,4 @@ -using TINK.Model.Repository.Response; +using TINK.Repository.Response; namespace TINK.Repository.Exception { diff --git a/TINKLib/Repository/Exception/UnsupportedCopriVersionDetectedException.cs b/TINKLib/Repository/Exception/UnsupportedCopriVersionDetectedException.cs new file mode 100644 index 0000000..d9df42f --- /dev/null +++ b/TINKLib/Repository/Exception/UnsupportedCopriVersionDetectedException.cs @@ -0,0 +1,8 @@ +namespace TINK.Repository.Exception +{ + public class UnsupportedCopriVersionDetectedException : System.Exception + { + public UnsupportedCopriVersionDetectedException() : base("Unsupported app version detected.") + {} + } +} diff --git a/TINKLib/Repository/Exception/WebConnectFailureException.cs b/TINKLib/Repository/Exception/WebConnectFailureException.cs index a16096a..374dbba 100644 --- a/TINKLib/Repository/Exception/WebConnectFailureException.cs +++ b/TINKLib/Repository/Exception/WebConnectFailureException.cs @@ -1,4 +1,4 @@ -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public class WebConnectFailureException : CommunicationException { diff --git a/TINKLib/Repository/Exception/WebExceptionHelper.cs b/TINKLib/Repository/Exception/WebExceptionHelper.cs index e88c24c..dfbfd75 100644 --- a/TINKLib/Repository/Exception/WebExceptionHelper.cs +++ b/TINKLib/Repository/Exception/WebExceptionHelper.cs @@ -1,42 +1,53 @@ using System.Net; -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public static class WebExceptionHelper { /// Gets if a exception is caused by an error connecting to copri (LAN or mobile data off/ not reachable, proxy, ...). - /// Expection to check. + /// Expection to check. /// True if exception if caused by an connection error. - public static bool GetIsConnectFailureException(this System.Exception p_oException) + public static bool GetIsConnectFailureException(this System.Exception exception) { - var l_oException = p_oException as WebException; - if (l_oException == null) +#if !USCSHARP9 + if (!(exception is WebException webException)) +#else + if (exception is not WebException webException) +#endif { return false; } - return l_oException.Status == WebExceptionStatus.ConnectFailure // Happens if WLAN and mobile data is off/ Router denies internet access/ ... - || l_oException.Status == WebExceptionStatus.NameResolutionFailure // Happens sometimes when not WLAN and no mobil connection are available (bad connection in lift). - || l_oException.Status == WebExceptionStatus.ReceiveFailure; // Happened when modile was connected to WLAN + return webException.Status == WebExceptionStatus.ConnectFailure // Happens if WLAN and mobile data is off/ Router denies internet access/ ... + || webException.Status == WebExceptionStatus.NameResolutionFailure // Happens sometimes when not WLAN and no mobil connection are available (bad connection in lift). + || webException.Status == WebExceptionStatus.ReceiveFailure; // Happened when modile was connected to WLAN } /// Gets if a exception is caused by clicking too fast. - /// Expection to check. + /// Expection to check. /// True if exception if caused by a fast click sequence. - public static bool GetIsForbiddenException(this System.Exception p_oException) + public static bool GetIsForbiddenException(this System.Exception exception) { - if (!(p_oException is WebException l_oException)) +#if !USCSHARP9 + if (!(exception is WebException webException)) +#else + if (exception is not WebException webException) +#endif { return false; } - if (!(l_oException?.Response is HttpWebResponse l_oResponse)) +#if !USCSHARP9 + if (!(webException?.Response is HttpWebResponse response)) +#else + if (webException?.Response is not HttpWebResponse response) +#endif { return false; } - return l_oException.Status == WebExceptionStatus.ProtocolError - && l_oResponse.StatusCode == HttpStatusCode.Forbidden; + return webException.Status == WebExceptionStatus.ProtocolError + && response.StatusCode == HttpStatusCode.Forbidden; } } } diff --git a/TINKLib/Repository/Exception/WebForbiddenException.cs b/TINKLib/Repository/Exception/WebForbiddenException.cs index 6c95c2f..408da74 100644 --- a/TINKLib/Repository/Exception/WebForbiddenException.cs +++ b/TINKLib/Repository/Exception/WebForbiddenException.cs @@ -1,4 +1,4 @@ -namespace TINK.Model.Repository.Exception +namespace TINK.Repository.Exception { public class WebForbiddenException : CommunicationException { diff --git a/TINKLib/Repository/ICopriServer.cs b/TINKLib/Repository/ICopriServer.cs index 399142a..252f672 100644 --- a/TINKLib/Repository/ICopriServer.cs +++ b/TINKLib/Repository/ICopriServer.cs @@ -1,10 +1,10 @@ using System; using System.Threading.Tasks; -using TINK.Model.Repository.Request; -using TINK.Model.Repository.Response; +using TINK.Model.Device; +using TINK.Repository.Request; using TINK.Repository.Response; -namespace TINK.Model.Repository +namespace TINK.Repository { /// Interface to communicate with copri server. public interface ICopriServerBase @@ -28,7 +28,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response on reserving request. Task DoReserveAsync( - int bikeId, + string bikeId, Uri operatorUri); /// Cancels reservation of bik. @@ -36,7 +36,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response on cancel reservation request. Task DoCancelReservationAsync( - int bikeId, + string bikeId, Uri operatorUri); /// Get authentication keys. @@ -44,7 +44,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response holding authentication keys. Task CalculateAuthKeysAsync( - int bikeId, + string bikeId, Uri operatorUri); /// Updates COPRI lock state for a booked bike. @@ -55,7 +55,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response on updating locking state. Task UpdateLockingStateAsync( - int bikeId, + string bikeId, LocationDto location, lock_state state, double batteryPercentage, @@ -68,7 +68,7 @@ namespace TINK.Model.Repository /// Holds the uri of the operator or null, in case of single operator setup. /// Response on booking request. Task DoBookAsync( - int bikeId, + string bikeId, Guid guid, double batteryPercentage, Uri operatorUri); @@ -76,19 +76,23 @@ namespace TINK.Model.Repository /// Returns a bike. /// Id of the bike to return. /// Geolocation of lock. + /// Provides info about hard and software. /// Holds the uri of the operator or null, in case of single operator setup. /// Response on returning request. Task DoReturn( - int bikeId, + string bikeId, LocationDto location, + ISmartDevice smartDevice, Uri operatorUri); /// /// Submits feedback to copri server. /// + /// Id of the bike to submit feedback for. /// True if bike is broken. /// General purpose message or error description. Task DoSubmitFeedback( + string bikeId, string message, bool isBikeBroken, Uri operatorUri); diff --git a/TINKLib/Repository/Request/IRequestBuilder.cs b/TINKLib/Repository/Request/IRequestBuilder.cs index e0bf258..047a9a8 100644 --- a/TINKLib/Repository/Request/IRequestBuilder.cs +++ b/TINKLib/Repository/Request/IRequestBuilder.cs @@ -1,6 +1,7 @@ using System; +using TINK.Model.Device; -namespace TINK.Model.Repository.Request +namespace TINK.Repository.Request { /// Defines members to create requests. public interface IRequestBuilder @@ -41,17 +42,17 @@ namespace TINK.Model.Repository.Request /// Gets reservation request (synonym: reservation == request == reservieren). /// Id of the bike to reserve. /// Requst to reserve bike. - string DoReserve(int bikeId); + string DoReserve(string bikeId); /// Gets request to cancel reservation. /// Id of the bike to cancel reservation for. /// Requst on cancel booking request. - string DoCancelReservation(int bikeId); + string DoCancelReservation(string bikeId); /// Request to get keys. /// Id of the bike to get keys for. /// Request to get keys. - string CalculateAuthKeys(int bikeId); + string CalculateAuthParameters(string bikeId); /// Gets the request for updating lock state for a booked bike. /// Id of the bike to update locking state for. @@ -59,7 +60,7 @@ namespace TINK.Model.Repository.Request /// New locking state. /// Request to update locking state. string UpateLockingState( - int bikeId, + string bikeId, LocationDto location, lock_state state, double batteryPercentage); @@ -69,20 +70,21 @@ namespace TINK.Model.Repository.Request /// Used to publish GUID from app to copri. Used for initial setup of bike in copri. /// Holds the filling level percentage of the battery. /// Request to booking bike. - string DoBook(int bikeId, Guid guid, double batteryPercentage); + string DoBook(string bikeId, Guid guid, double batteryPercentage); /// Gets request for returning the bike. /// Id of the bike to return. /// Geolocation of lock when returning bike. /// Requst on returning request. - string DoReturn(int bikeId, LocationDto location); + string DoReturn(string bikeId, LocationDto location, ISmartDevice smartDevice); /// /// Gets request for submiting feedback to copri server. /// + /// Id of the bike to which the feedback is related to. /// General purpose message or error description. /// True if bike is broken. - string DoSubmitFeedback(string message = null, bool isBikeBroken = false); + string DoSubmitFeedback(string bikeId, string message = null, bool isBikeBroken = false); } /// Copri locking states @@ -92,6 +94,7 @@ namespace TINK.Model.Repository.Request unlocked } + /// Holds lockation info. public class LocationDto { public double Latitude { get; private set; } diff --git a/TINKLib/Repository/Request/RequestBuilder.cs b/TINKLib/Repository/Request/RequestBuilder.cs index a7de790..a0f386d 100644 --- a/TINKLib/Repository/Request/RequestBuilder.cs +++ b/TINKLib/Repository/Request/RequestBuilder.cs @@ -1,8 +1,9 @@ using System; using System.Net; -using TINK.Model.Repository.Exception; +using TINK.Model.Device; +using TINK.Repository.Exception; -namespace TINK.Model.Repository.Request +namespace TINK.Repository.Request { /// Creates requests if no user is logged in. public class RequestBuilder : IRequestBuilder @@ -80,32 +81,34 @@ namespace TINK.Model.Repository.Request public string GetBikesOccupied() => throw new NotSupportedException(); /// Gets booking request response. - /// Id of the bike to book. + /// Id of the bike to book. /// Response on booking request. - public string DoReserve(int p_iBikeId) => throw new NotSupportedException(); + public string DoReserve(string bikeId) => throw new NotSupportedException(); /// Gets cancel booking request response. /// Id of the bike to book. /// Response on cancel booking request. - public string DoCancelReservation(int p_iBikeId) => throw new NotSupportedException(); + public string DoCancelReservation(string p_iBikeId) => throw new NotSupportedException(); /// Request to calculate authentication keys. /// Id of the bike to get keys for. /// Response on request. - public string CalculateAuthKeys(int bikeId) => throw new NotSupportedException(); + public string CalculateAuthParameters(string bikeId) => throw new NotSupportedException(); - public string UpateLockingState(int bikeId, LocationDto geolocation, lock_state state, double batteryPercentage) + public string UpateLockingState(string bikeId, LocationDto geolocation, lock_state state, double batteryPercentage) => throw new NotSupportedException(); - public string DoBook(int bikeId, Guid guid, double batteryPercentage) => throw new NotSupportedException(); + public string DoBook(string bikeId, Guid guid, double batteryPercentage) => throw new NotSupportedException(); - public string DoReturn(int bikeId, LocationDto geolocation) => throw new NotSupportedException(); + public string DoReturn(string bikeId, LocationDto geolocation, ISmartDevice smartDevice) => throw new NotSupportedException(); /// Gets submit feedback request. + /// Id of the bike to which the feedback is related to. /// General purpose message or error description. /// True if bike is broken. /// Submit feedback request. public string DoSubmitFeedback( + string bikeId, string message = null, bool isBikeBroken = false) => throw new NotSupportedException(); } diff --git a/TINKLib/Repository/Request/RequestBuilderLoggedIn.cs b/TINKLib/Repository/Request/RequestBuilderLoggedIn.cs index 8b74a10..d845b4c 100644 --- a/TINKLib/Repository/Request/RequestBuilderLoggedIn.cs +++ b/TINKLib/Repository/Request/RequestBuilderLoggedIn.cs @@ -1,9 +1,10 @@ using System; using System.Globalization; using System.Net; -using TINK.Model.Repository.Exception; +using TINK.Model.Device; +using TINK.Repository.Exception; -namespace TINK.Model.Repository.Request +namespace TINK.Repository.Request { /// Creates requests if a user is logged in. public class RequestBuilderLoggedIn : IRequestBuilder @@ -75,21 +76,21 @@ namespace TINK.Model.Repository.Request /// Operator specific call. /// Id of the bike to reserve. /// Requst to reserve bike. - public string DoReserve(int bikeId) + public string DoReserve(string bikeId) => $"request=booking_request&bike={bikeId}&authcookie={SessionCookie}{MerchantId}"; /// Gets request to cancel reservation. /// Operator specific call. /// Id of the bike to cancel reservation for. /// Requst on cancel booking request. - public string DoCancelReservation(int p_iBikeId) + public string DoCancelReservation(string p_iBikeId) => $"request=booking_cancel&bike={p_iBikeId}&authcookie={SessionCookie}{MerchantId}"; /// Request to get keys. /// Operator specific call. /// Id of the bike to get keys for. /// Request to get keys. - public string CalculateAuthKeys(int bikeId) + public string CalculateAuthParameters(string bikeId) => $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&genkey=1"; /// Gets the request for updating lock state for a booked bike. @@ -97,9 +98,9 @@ namespace TINK.Model.Repository.Request /// Id of the bike to update locking state for. /// New locking state. /// Request to update locking state. - public string UpateLockingState(int bikeId, LocationDto geolocation, lock_state state, double batteryPercentage) + public string UpateLockingState(string bikeId, LocationDto geolocation, lock_state state, double batteryPercentage) { - return $"request=booking_update&bike={bikeId}{GetLocationKey(geolocation)}&lock_state={state}{GetBatteryPercentageKey(batteryPercentage)}&authcookie={SessionCookie}{MerchantId}"; + return $"request=booking_update&bike={bikeId}{GetLocationParameters(geolocation)}&lock_state={state}{GetBatteryPercentageParameters(batteryPercentage)}&authcookie={SessionCookie}{MerchantId}"; } /// Gets booking request request (synonym: booking == renting == mieten). @@ -108,55 +109,65 @@ namespace TINK.Model.Repository.Request /// Used to publish GUID from app to copri. Used for initial setup of bike in copri. /// Holds the filling level percentage of the battery. /// Request to booking bike. - public string DoBook(int bikeId, Guid guid, double batteryPercentage) - => $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&Ilockit_GUID={guid}&state=occupied&lock_state=unlocked{GetBatteryPercentageKey(batteryPercentage)}"; + public string DoBook(string bikeId, Guid guid, double batteryPercentage) + => $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&Ilockit_GUID={guid}&state=occupied&lock_state=unlocked{GetBatteryPercentageParameters(batteryPercentage)}"; /// Gets request for returning the bike. /// Operator specific call. /// Id of bike to return. /// Geolocation of lock when returning bike. /// Requst on returning request. - public string DoReturn(int bikeId, LocationDto geolocation) + public string DoReturn(string bikeId, LocationDto geolocation, ISmartDevice smartDevice) { - return $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&state=available{GetLocationKey(geolocation)}&lock_state=locked"; + return $"request=booking_update" + + $"&bike={bikeId}" + + $"&authcookie={SessionCookie}{MerchantId}" + + $"&state=available" + + $"{GetLocationParameters(geolocation)}" + + $"&lock_state=locked" + + $"{GetSmartDeviceParameters(smartDevice)}"; } /// Gets submit feedback request. + /// Id of the bike to return. /// General purpose message or error description. /// True if bike is broken. /// Submit feedback request. public string DoSubmitFeedback( + string bikeId, string message = null, bool isBikeBroken = false) { if (string.IsNullOrEmpty(message) && !isBikeBroken) { // User just acknoledged biked returned message. - return "request=user_feedback"; + return $"request=user_feedback&bike={bikeId}&authcookie={SessionCookie}{MerchantId}"; } if (isBikeBroken == false) { // Bike is ok and user entered a feedback message. - return $"request=user_feedback&message={WebUtility.UrlEncode(message)}"; + return $"request=user_feedback&bike={bikeId}&message={WebUtility.UrlEncode(message)}&authcookie={SessionCookie}{MerchantId}"; } if (string.IsNullOrEmpty(message)) { // User just marked bike as broken without comment. - return $"request=user_feedback&bike_broken=1"; + return $"request=user_feedback&bike={bikeId}&bike_broken=1&authcookie={SessionCookie}{MerchantId}"; } // Bike is marked as broken and user added a comment. - return $"request=user_feedback&bike_broken=1&message={WebUtility.UrlEncode(message)}"; + return $"request=user_feedback&bike={bikeId}&bike_broken=1&message={WebUtility.UrlEncode(message)}&authcookie={SessionCookie}{MerchantId}"; } - private string GetBatteryPercentageKey(double batteryPercentage) => !double.IsNaN(batteryPercentage) + private string GetBatteryPercentageParameters(double batteryPercentage) => !double.IsNaN(batteryPercentage) ? $"&voltage={batteryPercentage.ToString(CultureInfo.InvariantCulture)}" : string.Empty; - private string GetLocationKey(LocationDto geolocation) + /// Gets the geolocation parameter. + /// Geolocation or null. + private string GetLocationParameters(LocationDto geolocation) { if (geolocation == null) return string.Empty; @@ -166,5 +177,16 @@ namespace TINK.Model.Repository.Request return $"&gps={geolocation.Latitude.ToString(CultureInfo.InvariantCulture)},{geolocation.Longitude.ToString(CultureInfo.InvariantCulture)}&gps_accuracy={geolocation.Accuracy.Value.ToString(CultureInfo.InvariantCulture)}&gps_age={geolocation.Age.TotalSeconds}"; } + + /// Gets the geolocation parameter. + /// Geolocation or null. + private string GetSmartDeviceParameters(ISmartDevice smartDevice) + => smartDevice != null + ? $"{(!string.IsNullOrEmpty(smartDevice.Manufacturer) ? $"&user_device_manufaturer={smartDevice.Manufacturer})" : string.Empty)}" + + $"{(!string.IsNullOrEmpty(smartDevice.Model) ? $"&user_device_model={smartDevice.Model}" : string.Empty)}" + + $"{(!string.IsNullOrEmpty(smartDevice.PlatformText) ? $"&user_device_platform={smartDevice.PlatformText}" : string.Empty)}" + + $"{(!string.IsNullOrEmpty(smartDevice.VersionText) ? $"&user_device_version={smartDevice.VersionText}" : string.Empty)}" + + $"{(!string.IsNullOrEmpty(smartDevice.Identifier) ? $"&user_device_id={smartDevice.Identifier}" : string.Empty)}" + : string.Empty; } } diff --git a/TINKLib/Repository/Response/AuthorizationResponse.cs b/TINKLib/Repository/Response/AuthorizationResponse.cs index 60a7a7f..e2c5179 100644 --- a/TINKLib/Repository/Response/AuthorizationResponse.cs +++ b/TINKLib/Repository/Response/AuthorizationResponse.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { [DataContract] public class AuthorizationResponse : ResponseBase @@ -10,6 +10,6 @@ namespace TINK.Model.Repository.Response /// Holds the group of the bike (TINK, Konrad, ...). [DataMember] - public string user_group { get; private set; } + public string[] user_group { get; private set; } } } diff --git a/TINKLib/Repository/Response/AuthorizationoutResponse.cs b/TINKLib/Repository/Response/AuthorizationoutResponse.cs index 5288756..66ceff8 100644 --- a/TINKLib/Repository/Response/AuthorizationoutResponse.cs +++ b/TINKLib/Repository/Response/AuthorizationoutResponse.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { [DataContract] public class AuthorizationoutResponse : ResponseBase diff --git a/TINKLib/Repository/Response/BikeInfoAvailable.cs b/TINKLib/Repository/Response/BikeInfoAvailable.cs index 49e940b..daddb38 100644 --- a/TINKLib/Repository/Response/BikeInfoAvailable.cs +++ b/TINKLib/Repository/Response/BikeInfoAvailable.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { [DataContract] public class BikeInfoAvailable : BikeInfoBase @@ -9,7 +9,7 @@ namespace TINK.Model.Repository.Response /// Position of the bike. /// [DataMember] - public string gps { get; private set; } + public GpsInfo gps { get; private set; } [DataMember] /// Full advertisement name. diff --git a/TINKLib/Repository/Response/BikeInfoBase.cs b/TINKLib/Repository/Response/BikeInfoBase.cs index b52f91a..55ca082 100644 --- a/TINKLib/Repository/Response/BikeInfoBase.cs +++ b/TINKLib/Repository/Response/BikeInfoBase.cs @@ -1,7 +1,6 @@ using System.Runtime.Serialization; -using TINK.Repository.Response; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { /// /// Holds info about a single bike. @@ -13,13 +12,13 @@ namespace TINK.Model.Repository.Response /// Id of the bike. /// [DataMember] - public int bike { get; private set; } + public string bike { get; private set; } /// /// Id of the station. /// [DataMember] - public int? station { get; private set; } + public string station { get; private set; } /// /// Holds the localized (german) description of the bike. @@ -32,7 +31,7 @@ namespace TINK.Model.Repository.Response /// Copri returns values "TINK", "Konrad". /// [DataMember] - public string bike_group { get; private set; } + public string[] bike_group { get; private set; } /// /// Rental state. diff --git a/TINKLib/Repository/Response/BikeInfoReservedBooked.cs b/TINKLib/Repository/Response/BikeInfoReservedBooked.cs index bd82b9f..402a5dc 100644 --- a/TINKLib/Repository/Response/BikeInfoReservedBooked.cs +++ b/TINKLib/Repository/Response/BikeInfoReservedBooked.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { [DataContract] public class BikeInfoReservedOrBooked : BikeInfoAvailable diff --git a/TINKLib/Repository/Response/BikesAvailableResponse.cs b/TINKLib/Repository/Response/BikesAvailableResponse.cs index d293445..862eff8 100644 --- a/TINKLib/Repository/Response/BikesAvailableResponse.cs +++ b/TINKLib/Repository/Response/BikesAvailableResponse.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { /// /// Holds the information about all bikes and is used for deserialization of copri answer. @@ -13,6 +13,6 @@ namespace TINK.Model.Repository.Response /// Dictionary of bikes. /// [DataMember] - public Dictionary bikes { get; private set; } + public Dictionary bikes { get; private set; } } } diff --git a/TINKLib/Repository/Response/BikesReservedOccupiedResponse.cs b/TINKLib/Repository/Response/BikesReservedOccupiedResponse.cs index 3ed5715..4802031 100644 --- a/TINKLib/Repository/Response/BikesReservedOccupiedResponse.cs +++ b/TINKLib/Repository/Response/BikesReservedOccupiedResponse.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { public class BikesReservedOccupiedResponse : ResponseBase { @@ -9,6 +9,6 @@ namespace TINK.Model.Repository.Response /// Dictionary of bikes. /// [DataMember] - public Dictionary bikes_occupied { get; private set; } + public Dictionary bikes_occupied { get; private set; } } } diff --git a/TINKLib/Repository/Response/CopriVersion.cs b/TINKLib/Repository/Response/CopriVersion.cs new file mode 100644 index 0000000..785b6f4 --- /dev/null +++ b/TINKLib/Repository/Response/CopriVersion.cs @@ -0,0 +1,11 @@ +using System.Runtime.Serialization; + +namespace TINK.Repository.Response +{ + [DataContract] + public class CopriVersion + { + [DataMember] + public string copri_version { get; private set; } + } +} diff --git a/TINKLib/Repository/Response/GpsInfo.cs b/TINKLib/Repository/Response/GpsInfo.cs new file mode 100644 index 0000000..d9f0616 --- /dev/null +++ b/TINKLib/Repository/Response/GpsInfo.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace TINK.Repository.Response +{ + /// + /// Holds info about a single bike. + /// + [DataContract] + public class GpsInfo + { + /// + /// Latitude position of the bike. + /// + [DataMember] + public string latitude { get; private set; } + + /// + /// Longitude position of the bike. + /// + [DataMember] + public string longitude { get; private set; } + } +} diff --git a/TINKLib/Repository/Response/JsonConvert.cs b/TINKLib/Repository/Response/JsonConvertRethrow.cs similarity index 95% rename from TINKLib/Repository/Response/JsonConvert.cs rename to TINKLib/Repository/Response/JsonConvertRethrow.cs index 0777bc6..d675203 100644 --- a/TINKLib/Repository/Response/JsonConvert.cs +++ b/TINKLib/Repository/Response/JsonConvertRethrow.cs @@ -2,7 +2,7 @@ namespace TINK.Repository.Response { - public static class JsonConvert + public static class JsonConvertRethrow { /// /// Deserializes COPRI responses in a consitent way for entire app. diff --git a/TINKLib/Repository/Response/ReservationBookingResponse.cs b/TINKLib/Repository/Response/ReservationBookingResponse.cs index d2b9547..06556a0 100644 --- a/TINKLib/Repository/Response/ReservationBookingResponse.cs +++ b/TINKLib/Repository/Response/ReservationBookingResponse.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { /// /// Holds the information about a booking request and is used for deserialization of copri answer. diff --git a/TINKLib/Repository/Response/ReservationCancelReturnResponse.cs b/TINKLib/Repository/Response/ReservationCancelReturnResponse.cs index 67f5ba7..5088565 100644 --- a/TINKLib/Repository/Response/ReservationCancelReturnResponse.cs +++ b/TINKLib/Repository/Response/ReservationCancelReturnResponse.cs @@ -1,5 +1,5 @@  -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { /// /// Holds the information about a cancel booking request and is used for deserialization of copri answer. diff --git a/TINKLib/Repository/Response/ResponseBase.cs b/TINKLib/Repository/Response/ResponseBase.cs index ba4adff..66abf26 100644 --- a/TINKLib/Repository/Response/ResponseBase.cs +++ b/TINKLib/Repository/Response/ResponseBase.cs @@ -1,9 +1,9 @@ using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { [DataContract] - public class ResponseBase + public class ResponseBase : CopriVersion { [DataMember] public string response_state { get; private set; } @@ -17,9 +17,6 @@ namespace TINK.Model.Repository.Response [DataMember] public string authcookie { get; private set; } - [DataMember] - public string copri_version { get; private set; } - /// Textual description of response. public new string ToString() { diff --git a/TINKLib/Repository/Response/ResponseContainer.cs b/TINKLib/Repository/Response/ResponseContainer.cs index 29fef86..537faee 100644 --- a/TINKLib/Repository/Response/ResponseContainer.cs +++ b/TINKLib/Repository/Response/ResponseContainer.cs @@ -1,12 +1,12 @@ using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { [DataContract] public class ResponseContainer { [DataMember] - public T tinkjson { get; private set; } + public T shareejson { get; private set; } /// /// Serializes object to string. @@ -14,12 +14,12 @@ namespace TINK.Model.Repository.Response /// public override string ToString() { - if (tinkjson == null) + if (shareejson == null) { return "Response container does not hold no entry."; } - return tinkjson.ToString(); + return shareejson.ToString(); } } } diff --git a/TINKLib/Repository/Response/ResponseHelper.cs b/TINKLib/Repository/Response/ResponseHelper.cs index 120ad38..eabd3c3 100644 --- a/TINKLib/Repository/Response/ResponseHelper.cs +++ b/TINKLib/Repository/Response/ResponseHelper.cs @@ -1,9 +1,8 @@ using System.Linq; -using TINK.Model.Repository.Exception; -using TINK.MultilingualResources; using TINK.Repository.Exception; +using TINK.MultilingualResources; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { public static class ResponseHelper { @@ -83,7 +82,7 @@ namespace TINK.Model.Repository.Response /// public static BikeInfoReservedOrBooked GetIsReserveResponseOk( this ReservationBookingResponse bookingResponse, - int bikeId) + string bikeId) { GetIsResponseOk(bookingResponse, string.Format(AppResources.ExceptionTextReservationBikeFailedGeneral, bikeId)); @@ -111,7 +110,7 @@ namespace TINK.Model.Repository.Response /// public static BikeInfoReservedOrBooked GetIsBookingResponseOk( this ReservationBookingResponse bookingResponse, - int bikeId) + string bikeId) { GetIsResponseOk(bookingResponse, string.Format(AppResources.ExceptionTextRentingBikeFailedGeneral, bikeId)); @@ -134,7 +133,7 @@ namespace TINK.Model.Repository.Response /// Verified response. public static T GetIsResponseOk(this T response, string textOfAction) where T : ResponseBase { - if (response == null) + if (response == null || response.response_state == null) { throw new InvalidResponseException(textOfAction, null); } @@ -161,7 +160,7 @@ namespace TINK.Model.Repository.Response /// Verified response. public static ReservationCancelReturnResponse GetIsCancelReservationResponseOk( this ReservationCancelReturnResponse cancelBookingResponse, - int bikeId) + string bikeId) { GetIsResponseOk(cancelBookingResponse, BIKES_CANCELREQUEST_ACTIONTEXT); @@ -184,7 +183,7 @@ namespace TINK.Model.Repository.Response /// Verified response. public static ReservationCancelReturnResponse GetIsReturnBikeResponseOk( this ReservationCancelReturnResponse returnBikeResponse, - int bikeId) + string bikeId) { // Check if bike is at station. if (NotAtStationException.IsNotAtStation(returnBikeResponse.response_state.ToUpper(), out NotAtStationException notAtStationException)) @@ -219,22 +218,8 @@ namespace TINK.Model.Repository.Response /// public static BikesReservedOccupiedResponse GetBikesOccupiedNone(string p_strSesstionCookie = null) { - var l_oJson = BIKES_OCCUPIED_REQUEST_NONE_FILE.Replace(@"""authcookie"": """"", @"""authcookie"": """ + (p_strSesstionCookie ?? string.Empty) + @""""); - return CopriCallsStatic.DeserializeBikesOccupiedResponse(l_oJson); + var l_oJson = CopriCallsMonkeyStore.BIKESOCCUPIED.Replace(@"""authcookie"": """"", @"""authcookie"": """ + (p_strSesstionCookie ?? string.Empty) + @""""); + return CopriCallsStatic.DeserializeResponse(@"{ ""shareejson"" : " + l_oJson + "}", (version) => new BikesReservedOccupiedResponse()); } - - /// - /// Holds an empty bikes occupied response. - /// - private const string BIKES_OCCUPIED_REQUEST_NONE_FILE = @" - { - ""tinkjson"": { - ""response_state"": ""OK"", - ""bikes_occupied"": { }, - ""authcookie"": """", - ""response"": ""user_bikes_occupied"", - ""apiserver"": ""https://tinkwwp.copri-bike.de"" - } - }"; } } diff --git a/TINKLib/Repository/Response/StationsAllResponse.cs b/TINKLib/Repository/Response/StationsAllResponse.cs index 696dd21..50d5568 100644 --- a/TINKLib/Repository/Response/StationsAllResponse.cs +++ b/TINKLib/Repository/Response/StationsAllResponse.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; -namespace TINK.Model.Repository.Response +namespace TINK.Repository.Response { /// /// Holds the information about all stations and is used for deserialization of copri answer. @@ -19,10 +19,10 @@ namespace TINK.Model.Repository.Response /// Unique id of the station. /// [DataMember] - public int station { get; private set; } + public string station { get; private set; } [DataMember] - public string station_group { get; private set; } + public string[] station_group { get; private set; } [DataMember] public string description { get; private set; } @@ -31,13 +31,13 @@ namespace TINK.Model.Repository.Response /// Position of the station. /// [DataMember] - public string gps { get; private set; } + public GpsInfo gps { get; private set; } } /// /// Dictionary of bikes. /// [DataMember] - public Dictionary stations { get; private set; } + public Dictionary stations { get; private set; } } } diff --git a/TINKLib/Repository/Response/SubmitFeedbackResponse.cs b/TINKLib/Repository/Response/SubmitFeedbackResponse.cs index 006e197..4c79035 100644 --- a/TINKLib/Repository/Response/SubmitFeedbackResponse.cs +++ b/TINKLib/Repository/Response/SubmitFeedbackResponse.cs @@ -1,6 +1,4 @@ -using TINK.Model.Repository.Response; - -namespace TINK.Repository.Response +namespace TINK.Repository.Response { public class SubmitFeedbackResponse : ResponseBase { diff --git a/TINKLib/Repository/Response/TariffDescription.cs b/TINKLib/Repository/Response/TariffDescription.cs index 620c98a..7defb1c 100644 --- a/TINKLib/Repository/Response/TariffDescription.cs +++ b/TINKLib/Repository/Response/TariffDescription.cs @@ -6,7 +6,11 @@ namespace TINK.Repository.Response /// Holds tariff info for a single bike. /// [DataContract] +#if USCSHARP9 public record TariffDescription +#else + public class TariffDescription +#endif { /// /// Name of the tariff. diff --git a/TINKLib/Repository/Response/VersionindependentResponse.cs b/TINKLib/Repository/Response/VersionindependentResponse.cs new file mode 100644 index 0000000..f757703 --- /dev/null +++ b/TINKLib/Repository/Response/VersionindependentResponse.cs @@ -0,0 +1,22 @@ +using System.Runtime.Serialization; + +namespace TINK.Repository.Response +{ + [DataContract] + public class VersionindependentResponse + { + private CopriVersion _shareejson; + + /// Root element for versions 4.0 and older. + [DataMember] + public CopriVersion tinkjson { get; private set; } + + /// Root element from 4.1 and later. + [DataMember] + public CopriVersion shareejson + { + get => _shareejson ?? tinkjson; + private set { _shareejson = value; } + } + } +} diff --git a/TINKLib/Services/CopriApi/CopriProviderHttps.cs b/TINKLib/Services/CopriApi/CopriProviderHttps.cs index 5344efe..a1ebc36 100644 --- a/TINKLib/Services/CopriApi/CopriProviderHttps.cs +++ b/TINKLib/Services/CopriApi/CopriProviderHttps.cs @@ -1,9 +1,9 @@ using Serilog; using System; using System.Threading.Tasks; -using TINK.Model.Repository; -using TINK.Model.Repository.Request; -using TINK.Model.Repository.Response; +using TINK.Model.Device; +using TINK.Repository; +using TINK.Repository.Request; using TINK.Repository.Response; namespace TINK.Model.Services.CopriApi @@ -123,9 +123,12 @@ namespace TINK.Model.Services.CopriApi try { Log.ForContext().Debug($"Querrying stations from copri."); + + var stations = await HttpsServer.GetStationsAsync(); + return new Result( typeof(CopriCallsHttps), - (await HttpsServer.GetStationsAsync()).GetIsResponseOk("Abfrage der Stationen fehlsgeschlagen.")); + stations.GetIsResponseOk("Abfrage der Stationen fehlsgeschlagen.")); } catch (Exception exception) { @@ -197,23 +200,23 @@ namespace TINK.Model.Services.CopriApi return await HttpsServer.DoAuthoutAsync(); } - public async Task DoReserveAsync(int p_iBikeId, Uri operatorUri) + public async Task DoReserveAsync(string bikeId, Uri operatorUri) { - return await HttpsServer.DoReserveAsync(p_iBikeId, operatorUri); + return await HttpsServer.DoReserveAsync(bikeId, operatorUri); } - public async Task DoCancelReservationAsync(int p_iBikeId, Uri operatorUri) + public async Task DoCancelReservationAsync(string bikeId, Uri operatorUri) { - return await HttpsServer.DoCancelReservationAsync(p_iBikeId, operatorUri); + return await HttpsServer.DoCancelReservationAsync(bikeId, operatorUri); } - public async Task CalculateAuthKeysAsync(int bikeId, Uri operatorUri) + public async Task CalculateAuthKeysAsync(string bikeId, Uri operatorUri) { return await HttpsServer.CalculateAuthKeysAsync(bikeId, operatorUri); } public async Task UpdateLockingStateAsync( - int bikeId, + string bikeId, LocationDto location, lock_state state, double batteryLevel, @@ -225,22 +228,27 @@ namespace TINK.Model.Services.CopriApi /// Used to publish GUID from app to copri. Used for initial setup of bike in copri. /// Holds the filling level percentage of the battery. /// Response on booking request. - public async Task DoBookAsync(int bikeId, Guid guid, double batteryPercentage, Uri operatorUri) + public async Task DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri) { return await HttpsServer.DoBookAsync(bikeId, guid, batteryPercentage, operatorUri); } - public async Task DoReturn(int bikeId, LocationDto location, Uri operatorUri) + public async Task DoReturn( + string bikeId, + LocationDto location, + ISmartDevice smartDevice, + Uri operatorUri) { - return await HttpsServer.DoReturn(bikeId, location, operatorUri); + return await HttpsServer.DoReturn(bikeId, location, smartDevice, operatorUri); } /// /// Submits feedback to copri server. /// + /// Id of the bike to submit feedback for. /// General purpose message or error description. /// True if bike is broken. - public async Task DoSubmitFeedback(string message, bool isBikeBroken, Uri opertorUri) => - await HttpsServer.DoSubmitFeedback(message, isBikeBroken, opertorUri); + public async Task DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri opertorUri) => + await HttpsServer.DoSubmitFeedback(bikeId, message, isBikeBroken, opertorUri); } } \ No newline at end of file diff --git a/TINKLib/Services/CopriApi/CopriProviderMonkeyStore.cs b/TINKLib/Services/CopriApi/CopriProviderMonkeyStore.cs index 44ed90c..3164470 100644 --- a/TINKLib/Services/CopriApi/CopriProviderMonkeyStore.cs +++ b/TINKLib/Services/CopriApi/CopriProviderMonkeyStore.cs @@ -1,8 +1,8 @@ using System; using System.Threading.Tasks; -using TINK.Model.Repository; -using TINK.Model.Repository.Request; -using TINK.Model.Repository.Response; +using TINK.Model.Device; +using TINK.Repository; +using TINK.Repository.Request; using TINK.Repository.Response; namespace TINK.Model.Services.CopriApi @@ -30,34 +30,38 @@ namespace TINK.Model.Services.CopriApi /// Gets the merchant id. public string MerchantId => monkeyStore.MerchantId; - public Task DoReserveAsync(int p_iBikeId, Uri operatorUri) + public Task DoReserveAsync(string bikeId, Uri operatorUri) => throw new NotSupportedException($"{nameof(DoReserveAsync)} is not cachable."); - public Task DoCancelReservationAsync(int p_iBikeId, Uri operatorUri) + public Task DoCancelReservationAsync(string bikeId, Uri operatorUri) => throw new NotSupportedException($"{nameof(DoCancelReservationAsync)} is not cachable."); - public Task CalculateAuthKeysAsync(int bikeId, Uri operatorUri) + public Task CalculateAuthKeysAsync(string bikeId, Uri operatorUri) => throw new NotSupportedException($"{nameof(CalculateAuthKeysAsync)} is not cachable."); public async Task UpdateLockingStateAsync( - int bikeId, + string bikeId, LocationDto geolocation, lock_state state, double batteryLevel, Uri operatorUri) => await monkeyStore.UpdateLockingStateAsync(bikeId, geolocation, state, batteryLevel, operatorUri); - public async Task DoBookAsync(int bikeId, Guid guid, double batteryPercentage, Uri operatorUri) + public async Task DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri) { return await monkeyStore.DoBookAsync(bikeId, guid, batteryPercentage, operatorUri); } - public async Task DoReturn(int bikeId, LocationDto geolocation, Uri operatorUri) + public async Task DoReturn( + string bikeId, + LocationDto geolocation, + ISmartDevice smartDevice, + Uri operatorUri) { - return await monkeyStore.DoReturn(bikeId, geolocation, operatorUri); + return await monkeyStore.DoReturn(bikeId, geolocation, smartDevice, operatorUri); } - public Task DoSubmitFeedback(string messge, bool bIsBikeBroke, Uri operatorUri) => throw new NotImplementedException(); + public Task DoSubmitFeedback(string bikeId, string messge, bool bIsBikeBroke, Uri operatorUri) => throw new NotImplementedException(); public async Task DoAuthorizationAsync(string p_strMailAddress, string p_strPassword, string p_strDeviceId) { diff --git a/TINKLib/Services/CopriApi/ICachedCopriServer.cs b/TINKLib/Services/CopriApi/ICachedCopriServer.cs index f688dbf..5e3ff8a 100644 --- a/TINKLib/Services/CopriApi/ICachedCopriServer.cs +++ b/TINKLib/Services/CopriApi/ICachedCopriServer.cs @@ -1,8 +1,7 @@  -using System; using System.Threading.Tasks; -using TINK.Model.Repository; -using TINK.Model.Repository.Response; +using TINK.Repository; +using TINK.Repository.Response; namespace TINK.Model.Services.CopriApi { diff --git a/TINKLib/Services/CopriApi/ICopriCache.cs b/TINKLib/Services/CopriApi/ICopriCache.cs index 24bd2cc..6a5f12e 100644 --- a/TINKLib/Services/CopriApi/ICopriCache.cs +++ b/TINKLib/Services/CopriApi/ICopriCache.cs @@ -1,5 +1,5 @@ -using TINK.Model.Repository; -using TINK.Model.Repository.Response; +using TINK.Repository; +using TINK.Repository.Response; namespace TINK.Model.Services.CopriApi { diff --git a/TINKLib/Services/CopriApi/ServerUris/CopriHelper.cs b/TINKLib/Services/CopriApi/ServerUris/CopriHelper.cs index b4f35f1..f0624ac 100644 --- a/TINKLib/Services/CopriApi/ServerUris/CopriHelper.cs +++ b/TINKLib/Services/CopriApi/ServerUris/CopriHelper.cs @@ -32,13 +32,13 @@ namespace TINK.Services.CopriApi.ServerUris /// Get the agb resource name name depending on host name. /// Host name. - /// AGB resource.. + /// AGB resource. public static string GetAGBResource(this string hostName) => $"{hostName.GetSiteFolderName()}/{(hostName.GetIsCopri()? "konrad-TINK-AGB" : "agb.html")}"; - /// Get the agb resource name name depending on host name. + /// Get the privacy resource name name depending on host name. /// Host name. - /// AGB resource.. + /// Privacy resource. public static string GetPrivacyResource(this string hostName) => $"{hostName.GetSiteFolderName()}/{(hostName.GetIsCopri() ? "Datenschutz" : "privacy.html")}"; } diff --git a/TINKLib/Services/CopriApi/ServerUris/CopriServerUriList.cs b/TINKLib/Services/CopriApi/ServerUris/CopriServerUriList.cs index edde6fc..97797fc 100644 --- a/TINKLib/Services/CopriApi/ServerUris/CopriServerUriList.cs +++ b/TINKLib/Services/CopriApi/ServerUris/CopriServerUriList.cs @@ -73,7 +73,11 @@ namespace TINK.Model.Services.CopriApi.ServerUris public Uri ActiveUri { get; } /// Gets the active uri. - public static Uri DevelopUri => new(TINK_DEVEL); +#if USCSHARP9 + public static Uri DevelopUri => new (TINK_DEVEL); +#else + public static Uri DevelopUri => new Uri(TINK_DEVEL); +#endif /// Gets the known uris. [JsonProperty] diff --git a/TINKLib/Services/IServicesContainer.cs b/TINKLib/Services/IServicesContainer.cs new file mode 100644 index 0000000..5c5daa1 --- /dev/null +++ b/TINKLib/Services/IServicesContainer.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace TINK.Services +{ + public interface IServicesContainer : IEnumerable + { + /// Get the active service. + T Active { get; } + + /// Sets service as active service by name. + /// Name of the new service obecs. + void SetActive(string active); + } +} diff --git a/TINKLib/Services/ServicesContainerMutable.cs b/TINKLib/Services/ServicesContainerMutable.cs index a456faa..e012407 100644 --- a/TINKLib/Services/ServicesContainerMutable.cs +++ b/TINKLib/Services/ServicesContainerMutable.cs @@ -8,7 +8,7 @@ namespace TINK.Services { /// Container of service objects (locks , geolocation, ...) where one service is active. /// All service objects must be of different type. - public class ServicesContainerMutable: IEnumerable, INotifyPropertyChanged + public class ServicesContainerMutable: IEnumerable, INotifyPropertyChanged, IServicesContainer { private readonly Dictionary serviceDict; diff --git a/TINKLib/TINKLib.csproj b/TINKLib/TINKLib.csproj index 754e503..3cb1d54 100644 --- a/TINKLib/TINKLib.csproj +++ b/TINKLib/TINKLib.csproj @@ -1,8 +1,5 @@  - - 9.0 - 4.0 en-GB @@ -16,25 +13,20 @@ en-GB - TRACE;USERFEEDBACKDLG_OFF + TRACE;USEFLYOUT - TRACE;USERFEEDBACKDLG_OFF + TRACE;USEFLYOUT - - - - - - - - - + + + + @@ -59,6 +51,7 @@ + diff --git a/TINKLib/View/IViewService.cs b/TINKLib/View/IViewService.cs index be1bb34..d3529bc 100644 --- a/TINKLib/View/IViewService.cs +++ b/TINKLib/View/IViewService.cs @@ -26,26 +26,45 @@ namespace TINK.View string cancel); /// Displays alert message. - /// Title of message. - /// Message to display. - /// Text of accept button. - /// Text of button. + /// Title of message. + /// Message to display. + /// Text of accept button. + /// Text of button. /// True if user pressed accept. - Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel); + Task DisplayAlert(string title, string message, string accept, string cancel); + + /// Displays alert message. + /// Title of message. + /// Message to display. + /// Detailed error description. + /// Text of accept button. + /// Text of button. + /// True if user pressed accept. + Task DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel); /// Displays an action sheet. /// Title of message. - /// Message to display. + /// Message to display. /// Text of button. /// /// Buttons holding options to select. /// T Task DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons); - /// Show a page. +#if USEMASTERDETAIL || USEFLYOUT + /// Shows a page. /// Type of page to show. /// Title of page to show. void ShowPage(ViewTypes type, string title = null); +#else + /// Shows a page. + /// Route of the page to show. +#if USCSHARP9 + public Task ShowPage(string route); +#else + Task ShowPage(string route); +#endif +#endif /// Pushes a page onto the stack. /// Page to display. @@ -60,6 +79,7 @@ namespace TINK.View Task DisplayUserFeedbackPopup(); +#if USCSHARP9 /// /// Feedback given by user when returning bike. /// @@ -78,5 +98,27 @@ namespace TINK.View /// string Message { get; set; } } +#endif } + +#if !USCSHARP9 + /// + /// Feedback given by user when returning bike. + /// + public interface IUserFeedback + { + /// + /// Holds whether bike is broken or not. + /// + bool IsBikeBroken { get; set; } + + /// + /// Holds either + /// - general feedback + /// - error description of broken bike + /// or both. + /// + string Message { get; set; } + } +#endif } diff --git a/TINKLib/View/MasterDetail/EmptyNavigationMasterDetail.cs b/TINKLib/View/MasterDetail/EmptyNavigationMasterDetail.cs index 055f6ce..43a1d79 100644 --- a/TINKLib/View/MasterDetail/EmptyNavigationMasterDetail.cs +++ b/TINKLib/View/MasterDetail/EmptyNavigationMasterDetail.cs @@ -1,8 +1,10 @@ -using Serilog; +#if USEMASTERDETAIL || USEFLYOUT +using Serilog; using System; namespace TINK.View.MasterDetail { + public class EmptyNavigationMasterDetail : INavigationMasterDetail { public bool IsGestureEnabled { set => Log.ForContext().Error($"Unexpected call of {nameof(IsGestureEnabled)} detected."); } @@ -13,3 +15,4 @@ namespace TINK.View.MasterDetail } } } +#endif diff --git a/TINKLib/View/MasterDetail/IDetailPage.cs b/TINKLib/View/MasterDetail/IDetailPage.cs index a3ffec8..acba661 100644 --- a/TINKLib/View/MasterDetail/IDetailPage.cs +++ b/TINKLib/View/MasterDetail/IDetailPage.cs @@ -1,4 +1,5 @@ -using TINK.View.MasterDetail; +#if USEMASTERDETAIL || USEFLYOUT +using TINK.View.MasterDetail; namespace TINK.View { @@ -13,3 +14,4 @@ namespace TINK.View INavigationMasterDetail NavigationMasterDetail { set; } } } +#endif diff --git a/TINKLib/View/MasterDetail/INavigationMasterDetail.cs b/TINKLib/View/MasterDetail/INavigationMasterDetail.cs index 84e53da..0d20e75 100644 --- a/TINKLib/View/MasterDetail/INavigationMasterDetail.cs +++ b/TINKLib/View/MasterDetail/INavigationMasterDetail.cs @@ -1,4 +1,5 @@ -using System; +#if USEMASTERDETAIL || USEFLYOUT +using System; namespace TINK.View.MasterDetail { @@ -16,3 +17,4 @@ namespace TINK.View.MasterDetail bool IsGestureEnabled { set; } } } +#endif diff --git a/TINKLib/ViewModel/Account/AccountPageViewModel.cs b/TINKLib/ViewModel/Account/AccountPageViewModel.cs index 2cb2a36..ebdc07c 100644 --- a/TINKLib/ViewModel/Account/AccountPageViewModel.cs +++ b/TINKLib/ViewModel/Account/AccountPageViewModel.cs @@ -7,11 +7,12 @@ using System.ComponentModel; using System.Threading.Tasks; using TINK.Model; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.View; using TINK.ViewModel.Settings; using System.Linq; using TINK.MultilingualResources; +using TINK.ViewModel.Info; namespace TINK.ViewModel.Account { @@ -25,7 +26,7 @@ namespace TINK.ViewModel.Account /// /// Reference on view servcie to show modal notifications and to perform navigation. /// - private IViewService m_oViewService; + private readonly IViewService m_oViewService; /// /// Holds the exception which occurred getting bikes occupied information. @@ -282,6 +283,17 @@ namespace TINK.ViewModel.Account IsConnected = TinkApp.GetIsConnected(); await TinkApp.GetConnector(IsConnected).Command.DoLogout(); } + catch (UnsupportedCopriVersionDetectedException) + { + await m_oViewService.DisplayAlert( + AppResources.MessageLogoutErrorTitle, + string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), + AppResources.MessageAnswerOk); + + // Restart polling again. + await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable()); + return; + } catch (Exception l_oException) { // Copri server is not reachable. @@ -315,7 +327,11 @@ namespace TINK.ViewModel.Account try { // Switch to map view after log out. +#if USEMASTERDETAIL || USEFLYOUT m_oViewService.ShowPage(ViewTypes.MapPage); +#else + await m_oViewService.ShowPage("//MapPage"); +#endif } catch (Exception p_oException) { diff --git a/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs index ea39f43..4838dd0 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BC/BikeViewModel.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using TINK.Model.Connector; +using TINK.Model.Device; using TINK.Model.User; using TINK.View; using TINK.ViewModel.Bikes.Bike.BC.RequestHandler; @@ -30,6 +31,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC /// /// Constructs a bike view model object. /// + /// Provides info about the smart device (phone, tablet, ...). /// Bike to be displayed. /// Object holding logged in user or an empty user object. /// Provides in use state information. @@ -37,13 +39,14 @@ namespace TINK.ViewModel.Bikes.Bike.BC public BikeViewModel( Func isConnectedDelegate, Func connectorFactory, - Action bikeRemoveDelegate, + Action bikeRemoveDelegate, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, BikeInfoMutable selectedBike, IUser activeUser, IInUseStateInfoProvider stateInfoProvider, - IBikesViewModel bikesViewModel) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, viewService, selectedBike, activeUser, stateInfoProvider, bikesViewModel) + IBikesViewModel bikesViewModel) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, activeUser, stateInfoProvider, bikesViewModel) { RequestHandler = activeUser.IsLoggedIn ? RequestHandlerFactory.Create( @@ -51,6 +54,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC isConnectedDelegate, connectorFactory, viewUpdateManager, + smartDevice, viewService, bikesViewModel, ActiveUser) @@ -74,6 +78,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, + SmartDevice, ViewService, BikesViewModel, ActiveUser); diff --git a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Base.cs b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Base.cs index 2c0b85c..cfe2c07 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Base.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Base.cs @@ -1,5 +1,6 @@ using System; using TINK.Model.Connector; +using TINK.Model.Device; using TINK.Model.State; using TINK.Model.User; using TINK.View; @@ -26,6 +27,9 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler /// public string ButtonText { get; } + /// Provides info about the smart device (phone, tablet, ...). + protected ISmartDevice SmartDevice; + /// /// Reference on view servcie to show modal notifications and to perform navigation. /// @@ -70,6 +74,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler /// Constructs the reqest handler base. /// /// Bike which is reserved or for which reservation is canceled. + /// Provides info about the smart device (phone, tablet, ...). /// View model to be used by subclasses for progress report and unlocking/ locking view. public Base( T selectedBike, @@ -78,6 +83,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler Func isConnectedDelegate, Func connectorFactory, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) @@ -88,6 +94,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler IsConnectedDelegate = isConnectedDelegate; ConnectorFactory = connectorFactory; ViewUpdateManager = viewUpdateManager; + SmartDevice = smartDevice; ViewService = viewService; ActiveUser = activeUser; IsRemoveBikeRequired = false; diff --git a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Disposable.cs b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Disposable.cs index 8711324..c1de23e 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Disposable.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Disposable.cs @@ -2,16 +2,18 @@ using System; using System.Threading.Tasks; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.State; using TINK.Model.User; using TINK.MultilingualResources; using TINK.View; using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public class Disposable : Base, IRequestHandler { @@ -20,13 +22,14 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler Func isConnectedDelegate, Func connectorFactory, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, - IUser activeUser) : base(selectedBike, selectedBike.State.Value.GetActionText(), true, isConnectedDelegate, connectorFactory, viewUpdateManager, viewService, bikesViewModel, activeUser) + IUser activeUser) : base(selectedBike, selectedBike.State.Value.GetActionText(), true, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser) { } - /// Gets the bike state. + /// Gets the bike state. public override InUseStateEnum State => InUseStateEnum.Disposable; /// Request bike. @@ -38,7 +41,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler var l_oResult = await ViewService.DisplayAlert( string.Empty, - string.Format(AppResources.QuestionReserveBike, SelectedBike.GetDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes), + string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes), AppResources.MessageAnswerYes, AppResources.MessageAnswerNo); @@ -110,7 +113,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler Log.ForContext().Information("User reserved bike {l_oId} successfully.", SelectedBike.Id); - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/NotLoggedIn.cs b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/NotLoggedIn.cs index d623761..4952239 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/NotLoggedIn.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/NotLoggedIn.cs @@ -66,8 +66,13 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler try { - // Switch to map page + // Switch to login page +#if USEMASTERDETAIL || USEFLYOUT ViewService.ShowPage(ViewTypes.LoginPage); +#else + await ViewService.ShowPage("//LoginPage"); +#endif + } catch (Exception p_oException) { diff --git a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Reserved.cs b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Reserved.cs index 2246373..dc9acb3 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Reserved.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandler/Reserved.cs @@ -2,27 +2,30 @@ using System; using System.Threading.Tasks; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.State; using TINK.Model.User; using TINK.MultilingualResources; using TINK.View; using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler { public class Reserved : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public Reserved( BikeInfoMutable selectedBike, Func isConnectedDelegate, Func connectorFactory, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, - IUser activeUser) : base(selectedBike, AppResources.ActionCancelRequest, true, isConnectedDelegate, connectorFactory, viewUpdateManager, viewService, bikesViewModel, activeUser) + IUser activeUser) : base(selectedBike, AppResources.ActionCancelRequest, true, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser) { } @@ -39,7 +42,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler BikesViewModel.ActionText = string.Empty; var l_oResult = await ViewService.DisplayAlert( string.Empty, - string.Format("Reservierung für Fahrrad {0} aufheben?", SelectedBike.GetDisplayName()), + string.Format("Reservierung für Fahrrad {0} aufheben?", SelectedBike.GetFullDisplayName()), "Ja", "Nein"); @@ -109,7 +112,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler Log.ForContext().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id); BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandlerFactory.cs b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandlerFactory.cs index 24873fd..2a5aef1 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandlerFactory.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BC/RequestHandlerFactory.cs @@ -1,5 +1,6 @@ using System; using TINK.Model.Connector; +using TINK.Model.Device; using TINK.Model.User; using TINK.View; using TINK.ViewModel.Bikes.Bike.BC.RequestHandler; @@ -9,12 +10,14 @@ namespace TINK.ViewModel.Bikes.Bike.BC { public static class RequestHandlerFactory { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public static IRequestHandler Create( BikeInfoMutable selectedBike, Func isConnectedDelegate, Func connectorFactory, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) @@ -28,6 +31,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC isConnectedDelegate, connectorFactory, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -39,6 +43,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC isConnectedDelegate, connectorFactory, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); diff --git a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs index da4840e..67bce38 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelBase.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using TINK.Model.Connector; +using TINK.Model.Device; using TINK.Model.State; using TINK.Model.User; using TINK.MultilingualResources; @@ -25,6 +26,9 @@ namespace TINK.ViewModel.Bikes.Bike /// public const string TIMEFORMAT = "dd. MMMM HH:mm"; + /// Provides info about the smart device (phone, tablet, ...). + protected ISmartDevice SmartDevice; + /// /// Reference on view servcie to show modal notifications and to perform navigation. /// @@ -37,7 +41,7 @@ namespace TINK.ViewModel.Bikes.Bike protected Func IsConnectedDelegate { get; } /// Removes bike from bikes view model. - protected Action BikeRemoveDelegate { get; } + protected Action BikeRemoveDelegate { get; } /// Object to manage update of view model objects from Copri. public Func ViewUpdateManager { get; } @@ -74,6 +78,7 @@ namespace TINK.ViewModel.Bikes.Bike /// /// Constructs a bike view model object. /// + /// Provides info about the smart device (phone, tablet, ...). /// Bike to be displayed. /// Object holding logged in user or an empty user object. /// Provides in use state information. @@ -81,15 +86,15 @@ namespace TINK.ViewModel.Bikes.Bike public BikeViewModelBase( Func isConnectedDelegate, Func connectorFactory, - Action bikeRemoveDelegate, + Action bikeRemoveDelegate, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, BikeInfoMutable selectedBike, IUser activeUser, IInUseStateInfoProvider stateInfoProvider, IBikesViewModel bikesViewModel) { - IsConnectedDelegate = isConnectedDelegate; ConnectorFactory = connectorFactory; @@ -98,6 +103,8 @@ namespace TINK.ViewModel.Bikes.Bike ViewUpdateManager = viewUpdateManager; + SmartDevice = smartDevice; + ViewService = viewService; bike = selectedBike @@ -153,21 +160,18 @@ namespace TINK.ViewModel.Bikes.Bike /// /// Gets the display name of the bike containing of bike id and type of bike.. /// - public string Name - { - get - { - return bike.GetDisplayName(); - } - } + public string Name => bike.GetDisplayName(); + + + /// + /// Gets the unique Id of bike or an empty string, if no name is defined to avoid duplicate display of id. + /// + public string DisplayId => bike.GetDisplayId(); /// /// Gets the unique Id of bike used by derived model to determine which bike to remove. /// - public int Id - { - get { return bike.Id; } - } + public string Id=> bike.Id; /// /// Returns status of a bike as text. @@ -240,7 +244,7 @@ namespace TINK.ViewModel.Bikes.Bike /// Display text private string GetReservedInfo( TimeSpan? p_oRemainingTime, - int? p_strStation = null, + string p_strStation = null, string p_strCode = null) { return StateInfoProvider.GetReservedInfo(p_oRemainingTime, p_strStation, p_strCode); @@ -254,7 +258,7 @@ namespace TINK.ViewModel.Bikes.Bike /// Display text private string GetBookedInfo( DateTime? p_oFrom, - int? p_strStation = null, + string p_strStation = null, string p_strCode = null) { return StateInfoProvider.GetBookedInfo(p_oFrom, p_strStation, p_strCode); diff --git a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs index b93af70..546e8be 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BikeViewModelFactory.cs @@ -4,11 +4,13 @@ using TINK.Services.BluetoothLock; using TINK.Model.Services.Geolocation; using TINK.Model.User; using TINK.View; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike { public static class BikeViewModelFactory { + /// Provides info about the smart device (phone, tablet, ...). /// Provides in use state information. /// View model to be used for progress report and unlocking/ locking view. public static BikeViewModelBase Create( @@ -16,8 +18,9 @@ namespace TINK.ViewModel.Bikes.Bike Func connectorFactory, IGeolocation geolocation, ILocksService lockService, - Action bikeRemoveDelegate, + Action bikeRemoveDelegate, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, Model.Bike.BC.BikeInfoMutable bikeInfo, IUser activeUser, @@ -32,6 +35,7 @@ namespace TINK.ViewModel.Bikes.Bike lockService, bikeRemoveDelegate, viewUpdateManager, + smartDevice, viewService, bikeInfo as Model.Bike.BluetoothLock.BikeInfoMutable, activeUser, @@ -42,6 +46,7 @@ namespace TINK.ViewModel.Bikes.Bike connectorFactory, bikeRemoveDelegate, viewUpdateManager, + smartDevice, viewService, bikeInfo, activeUser, diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs index 2c115a9..923fd7a 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/BikeViewModel.cs @@ -8,6 +8,7 @@ using TINK.Model.User; using TINK.View; using BikeInfoMutable = TINK.Model.Bike.BluetoothLock.BikeInfoMutable; using System.Threading.Tasks; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock { @@ -77,6 +78,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock /// /// Constructs a bike view model object. /// + /// Provides info about the smart device (phone, tablet, ...) /// Bike to be displayed. /// Object holding logged in user or an empty user object. /// Provides in use state information. @@ -86,13 +88,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock Func connectorFactory, IGeolocation geolocation, ILocksService lockService, - Action bikeRemoveDelegate, + Action bikeRemoveDelegate, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, BikeInfoMutable selectedBike, IUser user, IInUseStateInfoProvider stateInfoProvider, - IBikesViewModel bikesViewModel) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, viewService, selectedBike, user, stateInfoProvider, bikesViewModel) + IBikesViewModel bikesViewModel) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, user, stateInfoProvider, bikesViewModel) { RequestHandler = user.IsLoggedIn ? RequestHandlerFactory.Create( @@ -102,6 +105,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, user) @@ -132,6 +136,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock Geolocation, LockService, ViewUpdateManager, + SmartDevice, ViewService, BikesViewModel, ActiveUser); diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/Base.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/Base.cs index 0cd60ee..5d61594 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/Base.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/Base.cs @@ -5,6 +5,7 @@ using TINK.Model.Services.Geolocation; using TINK.Model.State; using TINK.View; using TINK.Model.User; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -14,6 +15,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler /// Constructs the reqest handler base. /// /// Bike which is reserved or for which reservation is canceled. + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public Base( Model.Bikes.Bike.BluetoothLock.IBikeInfoMutable selectedBike, @@ -24,9 +26,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, - IUser activeUser) : base(selectedBike, buttonText, isCopriButtonVisible, isConnectedDelegate, connectorFactory, viewUpdateManager, viewService, bikesViewModel, activeUser) + IUser activeUser) : base(selectedBike, buttonText, isCopriButtonVisible, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser) { Geolocation = geolocation ?? throw new ArgumentException($"Can not construct {GetType().Name}-object. Parameter {nameof(geolocation)} must not be null."); diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedClosed.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedClosed.cs index adb59a4..0178a1b 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedClosed.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedClosed.cs @@ -7,19 +7,20 @@ using TINK.View; using TINK.Model.Services.Geolocation; using TINK.Services.BluetoothLock; using Serilog; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Services.BluetoothLock.Exception; using TINK.MultilingualResources; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.User; -using TINK.Repository.Exception; using Xamarin.Essentials; -using TINK.Model.Repository.Request; +using TINK.Repository.Request; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { public class BookedClosed : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public BookedClosed( IBikeInfoMutable selectedBike, @@ -28,6 +29,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -38,7 +40,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler connectorFactory, geolocation, lockService, - viewUpdateManager, + viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -51,13 +54,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public override InUseStateEnum State => InUseStateEnum.Booked; /// Return bike. - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await ReturnBike(); + + /// Open bike and update COPRI lock state. + public async Task HandleRequestOption2() => await OpenLock(); + + + /// Return bike. + public async Task ReturnBike() { - BikesViewModel.IsIdle = false; + BikesViewModel.IsIdle = false; // Ask whether to really return bike? var l_oResult = await ViewService.DisplayAlert( string.Empty, - $"Fahrrad {SelectedBike.GetDisplayName()} zurückgeben?", + $"Fahrrad {SelectedBike.GetFullDisplayName()} zurückgeben?", "Ja", "Nein"); @@ -184,7 +194,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = ""; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User returned bike {bike} successfully.", SelectedBike); @@ -209,7 +219,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler try { await ConnectorFactory(IsConnected).Command.DoSubmitFeedback( - new UserFeedbackDto { IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message }, + new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message }, feedBackUri); } catch (Exception exception) @@ -226,12 +236,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler Log.ForContext().Error("User selected availalbe bike {bike} but reserving failed. {@l_oException}", SelectedBike.Id, exception); } + await ViewService.DisplayAlert( + AppResources.ErrorReturnSubmitFeedbackTitle, + AppResources.ErrorReturnSubmitFeedbackMessage, + AppResources.MessageAnswerOk); + + BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } #endif @@ -240,11 +256,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } /// Open bike and update COPRI lock state. - public async Task HandleRequestOption2() + public async Task OpenLock() { // Unlock bike. Log.ForContext().Information("User request to unlock bike {bike}.", SelectedBike); @@ -272,13 +288,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler AppResources.ErrorOpenLockOutOfReadMessage, "OK"); } - else if (exception is CouldntOpenBoldBlockedException) + else if (exception is CouldntOpenBoldIsBlockedException) { Log.ForContext().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception); await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, - AppResources.ErrorOpenLockMessage, + AppResources.ErrorOpenLockBoldBlockedMessage, + "OK"); + } + else if (exception is CouldntOpenBoldWasBlockedException) + { + Log.ForContext().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception); + + await ViewService.DisplayAlert( + AppResources.ErrorOpenLockStillOpenTitle, + AppResources.ErrorOpenLockBoldWasBlockedMessage, "OK"); } else if (exception is CouldntOpenInconsistentStateExecption inconsistentState @@ -311,13 +336,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -372,7 +397,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedDisconnected.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedDisconnected.cs index 1a915dc..89f648c 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedDisconnected.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedDisconnected.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using TINK.Model.Bike.BluetoothLock; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.Tdo; @@ -13,11 +13,13 @@ using TINK.Model.State; using TINK.MultilingualResources; using TINK.View; using TINK.Model.User; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { public class BookedDisconnected : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public BookedDisconnected( IBikeInfoMutable selectedBike, @@ -26,6 +28,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : @@ -38,6 +41,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -49,14 +53,23 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler /// Gets the bike state. public override InUseStateEnum State => InUseStateEnum.Booked; - public Task HandleRequestOption1() + public async Task HandleRequestOption1() => await UnsupportedRequest(); + + /// Scan for lock. + /// + public async Task HandleRequestOption2() => await ConnectLock(); + + /// Requst is not supported, button should be disabled. + /// + public async Task UnsupportedRequest() { - throw new NotSupportedException(); + Log.ForContext().Error("Click of unsupported button click detected."); + return await Task.FromResult(this); } /// Scan for lock. /// - public async Task HandleRequestOption2() + public async Task ConnectLock() { // Lock list to avoid multiple taps while copri action is pending. BikesViewModel.IsIdle = false; @@ -136,11 +149,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { Log.ForContext().Error("Lock can not be found. {Exception}", exception); - continueConnect = await ViewService.DisplayAlert( - "Fehler bei Verbinden mit Schloss!", - $"{AppResources.ErrorBookedSearchMessage}\r\nDetails:\r\n{exception.Message}", - "Wiederholen", - "Abbrechen"); + continueConnect = await ViewService.DisplayAdvancedAlert( + "Fehler bei Verbinden mit Schloss!", + AppResources.ErrorBookedSearchMessage, + exception.Message, + "Wiederholen", + "Abbrechen"); } if (continueConnect) @@ -187,7 +201,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedOpen.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedOpen.cs index 10808f7..111cc6b 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedOpen.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedOpen.cs @@ -7,14 +7,14 @@ using TINK.View; using TINK.Model.Services.Geolocation; using TINK.Services.BluetoothLock; using Serilog; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Services.BluetoothLock.Exception; using Xamarin.Essentials; using TINK.MultilingualResources; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.User; -using TINK.Model.Repository.Request; -using TINK.Repository.Exception; +using TINK.Repository.Request; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -28,6 +28,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -38,7 +39,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler connectorFactory, geolocation, lockService, - viewUpdateManager, + viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -51,14 +53,19 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public override InUseStateEnum State => InUseStateEnum.Disposable; /// Close lock and return bike. - /// - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await CloseLockAndReturnBike(); + + /// Close lock in order to pause ride and update COPRI lock state. + public async Task HandleRequestOption2() => await CloseLock(); + + /// Close lock and return bike. + public async Task CloseLockAndReturnBike() { // Ask whether to really return bike? BikesViewModel.IsIdle = false; var l_oResult = await ViewService.DisplayAlert( string.Empty, - $"Fahrrad {SelectedBike.GetDisplayName()} abschließen und zurückgeben?", + $"Fahrrad {SelectedBike.GetFullDisplayName()} abschließen und zurückgeben?", "Ja", "Nein"); @@ -132,7 +139,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } if (SelectedBike.LockInfo.State != LockingState.Closed) @@ -152,7 +159,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Get geoposition. @@ -179,7 +186,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Lock list to avoid multiple taps while copri action is pending. @@ -201,7 +208,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler Accuracy = currentLocation.Accuracy ?? double.NaN, Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime), }.Build() - : null); + : null, + SmartDevice); // If canceling bike succedes remove bike because it is not ready to be booked again IsRemoveBikeRequired = true; } @@ -261,7 +269,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User returned bike {bike} successfully.", SelectedBike); @@ -286,7 +294,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler try { await ConnectorFactory(IsConnected).Command.DoSubmitFeedback( - new UserFeedbackDto { IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message }, + new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message }, feedBackUri); } catch (Exception exception) @@ -303,12 +311,17 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler Log.ForContext().Error("User selected availalbe bike {bike} but reserving failed. {@l_oException}", SelectedBike.Id, exception); } + await ViewService.DisplayAlert( + AppResources.ErrorReturnSubmitFeedbackTitle, + AppResources.ErrorReturnSubmitFeedbackMessage, + AppResources.MessageAnswerOk); + BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } #endif @@ -316,11 +329,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } /// Close lock in order to pause ride and update COPRI lock state. - public async Task HandleRequestOption2() + public async Task CloseLock() { // Unlock bike. BikesViewModel.IsIdle = false; @@ -383,7 +396,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Get geoposition. @@ -450,7 +463,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedUnknown.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedUnknown.cs index f3f9410..597583f 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedUnknown.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/BookedUnknown.cs @@ -5,21 +5,22 @@ using TINK.Model.Bike.BluetoothLock; using TINK.Model.Connector; using TINK.Model.State; using TINK.View; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Services.Geolocation; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.MultilingualResources; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.User; -using TINK.Repository.Exception; using Xamarin.Essentials; -using TINK.Model.Repository.Request; +using TINK.Repository.Request; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { public class BookedUnknown : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...). /// View model to be used for progress report and unlocking/ locking view. public BookedUnknown( IBikeInfoMutable selectedBike, @@ -28,6 +29,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -39,6 +41,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -51,7 +54,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public override InUseStateEnum State => InUseStateEnum.Booked; /// Open bike and update COPRI lock state. - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await OpenLock(); + + /// Close lock in order to pause ride and update COPRI lock state. + public async Task HandleRequestOption2() => await CloseLock(); + + + /// Open bike and update COPRI lock state. + public async Task OpenLock() { // Unlock bike. Log.ForContext().Information("User request to unlock bike {bike}.", SelectedBike); @@ -79,13 +89,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler AppResources.ErrorOpenLockOutOfReadMessage, "OK"); } - else if (exception is CouldntOpenBoldBlockedException) + else if (exception is CouldntOpenBoldIsBlockedException) { Log.ForContext().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception); await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, - AppResources.ErrorOpenLockMessage, + AppResources.ErrorOpenLockBoldBlockedMessage, + "OK"); + } + else if (exception is CouldntOpenBoldWasBlockedException) + { + Log.ForContext().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception); + + await ViewService.DisplayAlert( + AppResources.ErrorOpenLockStillOpenTitle, + AppResources.ErrorOpenLockBoldWasBlockedMessage, "OK"); } else if (exception is CouldntOpenInconsistentStateExecption inconsistentState @@ -118,13 +137,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -179,11 +198,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } /// Close lock in order to pause ride and update COPRI lock state. - public async Task HandleRequestOption2() + public async Task CloseLock() { // Unlock bike. BikesViewModel.IsIdle = false; @@ -246,7 +265,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Get geoposition. @@ -313,7 +332,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableDisconnected.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableDisconnected.cs index 6d63a61..de4755c 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableDisconnected.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableDisconnected.cs @@ -5,7 +5,7 @@ using TINK.Model.Bike.BluetoothLock; using TINK.Model.Connector; using TINK.Model.State; using TINK.View; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Services.Geolocation; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Tdo; @@ -13,12 +13,13 @@ using TINK.MultilingualResources; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Model.User; -using TINK.Repository.Exception; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { public class DisposableDisconnected : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...). /// View model to be used for progress report and unlocking/ locking view. public DisposableDisconnected( IBikeInfoMutable selectedBike, @@ -27,6 +28,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -38,6 +40,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -50,14 +53,19 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public override InUseStateEnum State => InUseStateEnum.Disposable; /// Reserve bike and connect to lock. - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await ReserverBookAndOpen(); + + public async Task HandleRequestOption2() => await UnsupportedRequest(); + + /// Reserve bike and connect to lock. + public async Task ReserverBookAndOpen() { BikesViewModel.IsIdle = false; // Ask whether to really book bike? var alertResult = await ViewService.DisplayAlert( string.Empty, - string.Format(AppResources.QuestionReserveBike, SelectedBike.GetDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes), + string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes), AppResources.MessageAnswerYes, AppResources.MessageAnswerNo); @@ -154,7 +162,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } SelectedBike.LockInfo.State = result?.State?.GetLockingState() ?? LockingState.Disconnected; @@ -168,7 +176,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } SelectedBike.LockInfo.Guid = result?.Guid ?? new Guid(); @@ -179,7 +187,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler // Ask whether to really book bike? alertResult = await ViewService.DisplayAlert( string.Empty, - string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetDisplayName()), + string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()), AppResources.MessageAnswerYes, AppResources.MessageAnswerNo); @@ -206,7 +214,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User selected recently requested bike {bike} in order to book.", SelectedBike); @@ -247,7 +255,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Unlock bike. @@ -268,13 +276,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler AppResources.ErrorOpenLockOutOfReadMessage, "OK"); } - else if (exception is CouldntOpenBoldBlockedException) + else if (exception is CouldntOpenBoldIsBlockedException) { Log.ForContext().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception); await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, - AppResources.ErrorOpenLockMessage, + AppResources.ErrorOpenLockBoldBlockedMessage, + "OK"); + } + else if (exception is CouldntOpenBoldWasBlockedException) + { + Log.ForContext().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception); + + await ViewService.DisplayAlert( + AppResources.ErrorOpenLockStillOpenTitle, + AppResources.ErrorOpenLockBoldWasBlockedMessage, "OK"); } else if (exception is CouldntOpenInconsistentStateExecption inconsistentState @@ -305,7 +322,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } if (SelectedBike.LockInfo.State != LockingState.Open) @@ -316,13 +333,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = ""; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -377,12 +394,15 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } - public Task HandleRequestOption2() + /// Requst is not supported, button should be disabled. + /// + public async Task UnsupportedRequest() { - throw new NotSupportedException(); + Log.ForContext().Error("Click of unsupported button click detected."); + return await Task.FromResult(this); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableOpen.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableOpen.cs index 7673fd6..fc03e57 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableOpen.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/DisposableOpen.cs @@ -5,13 +5,14 @@ using TINK.Model.Bike.BluetoothLock; using TINK.Model.Connector; using TINK.Model.State; using TINK.View; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Services.Geolocation; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.MultilingualResources; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.User; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -32,6 +33,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler /// - no other device can access lock /// - app itself should never event attempt to open a lock which is not rented. /// + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public DisposableOpen( IBikeInfoMutable selectedBike, @@ -40,6 +42,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -51,6 +54,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -64,7 +68,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler /// Books bike by reserving bike, opening lock and booking bike. /// Next request handler. - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await DoBookOrClose(); + + public async Task HandleRequestOption2() => await UnsupportedRequest(); + + + /// Books bike by reserving bike, opening lock and booking bike. + /// Next request handler. + public async Task DoBookOrClose() { BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending. @@ -75,7 +86,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler // Ask whether to really book bike or close lock? var l_oResult = await ViewService.DisplayAlert( string.Empty, - $"Fahrrad {SelectedBike.GetDisplayName()} mieten oder Schloss schließen?", + $"Fahrrad {SelectedBike.GetFullDisplayName()} mieten oder Schloss schließen?", "Mieten", "Schloss schließen"); @@ -138,7 +149,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Disconnect lock. @@ -158,7 +169,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Lock list to avoid multiple taps while copri action is pending. @@ -167,7 +178,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -250,7 +261,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler // Update status text and unlock list of bikes because no more action is pending. BikesViewModel.ActionText = string.Empty; // Todo: Move this statement in front of finally block because in catch block BikesViewModel.ActionText is already set to empty. BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User reserved bike {bike} successfully.", SelectedBike); @@ -262,12 +273,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler // Update status text and unlock list of bikes because no more action is pending. BikesViewModel.ActionText = string.Empty; // Todo: Move this statement in front of finally block because in catch block BikesViewModel.ActionText is already set to empty. BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } - public async Task HandleRequestOption2() + /// Requst is not supported, button should be disabled. + /// + public async Task UnsupportedRequest() { - Log.ForContext().Error("Click of unsupported button detected."); + Log.ForContext().Error("Click of unsupported button click detected."); return await Task.FromResult(this); } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/InvalidState.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/InvalidState.cs index b752161..5ea50e6 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/InvalidState.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/InvalidState.cs @@ -30,7 +30,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public bool IsLockitButtonVisible => false; - public string LockitButtonText => this.GetType().Name; + public string LockitButtonText => GetType().Name; public bool IsConnected => false; @@ -40,7 +40,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public bool IsButtonVisible => false; - public string ButtonText => this.GetType().Name; + public string ButtonText => GetType().Name; /// Gets if the bike has to be remvoed after action has been completed. public bool IsRemoveBikeRequired => false; diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/NotLoggedIn.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/NotLoggedIn.cs index 4833020..89d9f0d 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/NotLoggedIn.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/NotLoggedIn.cs @@ -1,6 +1,5 @@ using Serilog; using System; -using System.ComponentModel; using System.Threading.Tasks; using TINK.Model.State; using TINK.View; @@ -66,7 +65,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock try { // Switch to map page +#if USEMASTERDETAIL || USEFLYOUT ViewService.ShowPage(ViewTypes.LoginPage); +#else + await ViewService.ShowPage("//LoginPage"); +#endif } catch (Exception p_oException) { diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedClosed.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedClosed.cs index b29b471..bb9865d 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedClosed.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedClosed.cs @@ -2,7 +2,7 @@ using System; using System.Threading.Tasks; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Bike.BluetoothLock; using TINK.Model.State; using TINK.View; @@ -13,7 +13,7 @@ using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.MultilingualResources; using TINK.Model.User; -using TINK.Repository.Exception; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -25,6 +25,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler /// public class ReservedClosed : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public ReservedClosed( IBikeInfoMutable selectedBike, @@ -33,6 +34,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -43,7 +45,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler connectorFactory, geolocation, lockService, - viewUpdateManager, + viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -56,13 +59,19 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public override InUseStateEnum State => InUseStateEnum.Reserved; /// Cancel reservation. - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await CancelReservation(); + + /// Open lock and book bike. + public async Task HandleRequestOption2() => await OpenLockAndDooBook(); + + /// Cancel reservation. + public async Task CancelReservation() { BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending. var l_oResult = await ViewService.DisplayAlert( string.Empty, - string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetDisplayName()), + string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()), AppResources.QuestionAnswerYes, AppResources.QuestionAnswerNo); @@ -125,7 +134,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = ""; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id); @@ -147,18 +156,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } /// Open lock and book bike. - public async Task HandleRequestOption2() + public async Task OpenLockAndDooBook() { BikesViewModel.IsIdle = false; // Ask whether to really book bike? var l_oResult = await ViewService.DisplayAlert( string.Empty, - string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetDisplayName()), + string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()), AppResources.MessageAnswerYes, AppResources.MessageAnswerNo); @@ -212,7 +221,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Unlock bike. @@ -233,13 +242,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler AppResources.ErrorOpenLockOutOfReadMessage, "OK"); } - else if (exception is CouldntOpenBoldBlockedException) + else if (exception is CouldntOpenBoldIsBlockedException) { Log.ForContext().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception); await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, - AppResources.ErrorOpenLockMessage, + AppResources.ErrorOpenLockBoldBlockedMessage, + "OK"); + } + else if (exception is CouldntOpenBoldWasBlockedException) + { + Log.ForContext().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception); + + await ViewService.DisplayAlert( + AppResources.ErrorOpenLockStillOpenTitle, + AppResources.ErrorOpenLockBoldWasBlockedMessage, "OK"); } else if (exception is CouldntOpenInconsistentStateExecption inconsistentState @@ -270,7 +288,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } if (SelectedBike.LockInfo.State != LockingState.Open) @@ -281,13 +299,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = ""; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -342,7 +360,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedDisconnected.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedDisconnected.cs index 188b585..302f99b 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedDisconnected.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedDisconnected.cs @@ -2,7 +2,7 @@ using System; using System.Threading.Tasks; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Bike.BluetoothLock; using TINK.Model.State; using TINK.View; @@ -13,12 +13,13 @@ using TINK.Services.BluetoothLock.Tdo; using TINK.MultilingualResources; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.User; -using TINK.Repository.Exception; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { public class ReservedDisconnected : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public ReservedDisconnected( IBikeInfoMutable selectedBike, @@ -27,6 +28,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -38,6 +40,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -50,13 +53,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public override InUseStateEnum State => InUseStateEnum.Reserved; /// Cancel reservation. - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await CancelReservation(); + + /// Connect to reserved bike ask whether to book bike bike or not and if yes open lock. + /// + public async Task HandleRequestOption2() => await ConnectLockAndBook(); + + /// Cancel reservation. + public async Task CancelReservation() { BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending. var alertResult = await ViewService.DisplayAlert( string.Empty, - string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetDisplayName()), + string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()), AppResources.QuestionAnswerYes, AppResources.QuestionAnswerNo); @@ -111,7 +121,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id); @@ -120,12 +130,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } - /// Connect to reserved bike. + /// Connect to reserved bike ask whether to book bike bike or not and if yes open lock. /// - public async Task HandleRequestOption2() + public async Task ConnectLockAndBook() { BikesViewModel.IsIdle = false; Log.ForContext().Information("Request to search for {bike} detected.", SelectedBike); @@ -209,11 +219,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler else { Log.ForContext().Error("Lock state can not be retrieved. {Exception}", exception); - continueConnect = await ViewService.DisplayAlert( - "Fehler bei Verbinden mit Schloss!", - $"{AppResources.ErrorReservedSearchMessage}\r\nDetails:\r\n{exception.Message}", - "Wiederholen", - "Abbrechen"); + continueConnect = await ViewService.DisplayAdvancedAlert( + "Fehler bei Verbinden mit Schloss!", + AppResources.ErrorReservedSearchMessage, + exception.Message, + "Wiederholen", + "Abbrechen"); } if (continueConnect) @@ -259,7 +270,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler // Ask whether to really book bike? var alertResult = await ViewService.DisplayAlert( string.Empty, - string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetDisplayName()), + string.Format(AppResources.MessageOpenLockAndBookeBike, SelectedBike.GetFullDisplayName()), AppResources.MessageAnswerYes, AppResources.MessageAnswerNo); @@ -286,7 +297,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User selected recently requested bike {bike} in order to book.", SelectedBike); @@ -327,7 +338,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Unlock bike. @@ -348,13 +359,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler AppResources.ErrorOpenLockOutOfReadMessage, "OK"); } - else if (exception is CouldntOpenBoldBlockedException) + else if (exception is CouldntOpenBoldIsBlockedException) { Log.ForContext().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception); await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, - AppResources.ErrorOpenLockMessage, + AppResources.ErrorOpenLockBoldBlockedMessage, + "OK"); + } + else if (exception is CouldntOpenBoldWasBlockedException) + { + Log.ForContext().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception); + + await ViewService.DisplayAlert( + AppResources.ErrorOpenLockStillOpenTitle, + AppResources.ErrorOpenLockBoldWasBlockedMessage, "OK"); } else if (exception is CouldntOpenInconsistentStateExecption inconsistentState @@ -385,7 +405,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } if (SelectedBike.LockInfo.State != LockingState.Open) @@ -396,13 +416,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = ""; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -459,7 +479,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedOpen.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedOpen.cs index 3f9f722..f91df16 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedOpen.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedOpen.cs @@ -2,7 +2,7 @@ using System; using System.Threading.Tasks; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Bike.BluetoothLock; using TINK.Model.State; using TINK.View; @@ -12,6 +12,7 @@ using TINK.Services.BluetoothLock.Exception; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.MultilingualResources; using TINK.Model.User; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -23,6 +24,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler /// - two devices can not simultaneously conect to same lock. public class ReservedOpen : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public ReservedOpen( IBikeInfoMutable selectedBike, @@ -31,6 +33,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -42,6 +45,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -54,13 +58,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler public override InUseStateEnum State => InUseStateEnum.Reserved; /// Cancel reservation. - public async Task HandleRequestOption1() + public async Task HandleRequestOption1() => await CloseLockOrDoBook(); + + /// Manage sound/ alarm settings. + /// + public async Task HandleRequestOption2() => await ManageLockSettings(); + + /// Cancel reservation. + public async Task CloseLockOrDoBook() { BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending. var l_oResult = await ViewService.DisplayAlert( string.Empty, - string.Format("Rad {0} abschließen und zurückgeben oder Rad mieten?", SelectedBike.GetDisplayName()), + string.Format("Rad {0} abschließen und zurückgeben oder Rad mieten?", SelectedBike.GetFullDisplayName()), "Zurückgeben", "Mieten"); @@ -76,7 +87,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -145,7 +156,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User booked bike {bike} successfully.", SelectedBike); @@ -155,7 +166,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Close lock and cancel reservation. @@ -214,7 +225,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = ""; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } BikesViewModel.ActionText = AppResources.ActivityTextCancelingReservation; @@ -255,7 +266,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = ""; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } Log.ForContext().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id); @@ -277,12 +288,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } /// Manage sound/ alarm settings. /// - public async Task HandleRequestOption2() + public async Task ManageLockSettings() { // Stop polling before requesting bike. BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; @@ -384,7 +395,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler exception.Message, "OK"); - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } finally { @@ -461,7 +472,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler exception.Message, "OK"); - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } finally { diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedUnknown.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedUnknown.cs index 3404ab6..746797c 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedUnknown.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/ReservedUnknown.cs @@ -5,21 +5,22 @@ using TINK.Model.Bike.BluetoothLock; using TINK.Model.Connector; using TINK.Model.State; using TINK.View; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Services.Geolocation; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.MultilingualResources; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.User; -using TINK.Repository.Exception; using Xamarin.Essentials; -using TINK.Model.Repository.Request; +using TINK.Repository.Request; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { public class ReservedUnknown : Base, IRequestHandler { + /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public ReservedUnknown( IBikeInfoMutable selectedBike, @@ -28,6 +29,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( @@ -39,6 +41,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser) @@ -79,13 +82,22 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler AppResources.ErrorOpenLockOutOfReadMessage, "OK"); } - else if (exception is CouldntOpenBoldBlockedException) + else if (exception is CouldntOpenBoldIsBlockedException) { Log.ForContext().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception); await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, - AppResources.ErrorOpenLockMessage, + AppResources.ErrorOpenLockBoldBlockedMessage, + "OK"); + } + else if (exception is CouldntOpenBoldWasBlockedException) + { + Log.ForContext().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception); + + await ViewService.DisplayAlert( + AppResources.ErrorOpenLockStillOpenTitle, + AppResources.ErrorOpenLockBoldWasBlockedMessage, "OK"); } else if (exception is CouldntOpenInconsistentStateExecption inconsistentState @@ -118,13 +130,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel; try { - SelectedBike.LockInfo.BatteryPercentage = (await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync()); + SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync(); } catch (Exception exception) { @@ -179,7 +191,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } /// Close lock in order to pause ride and update COPRI lock state. @@ -246,7 +258,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } // Get geoposition. @@ -313,7 +325,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler await ViewUpdateManager().StartUpdateAyncPeridically(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; - return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, ViewService, BikesViewModel, ActiveUser); + return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } } diff --git a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandlerFactory.cs b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandlerFactory.cs index 18910e0..bac189b 100644 --- a/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandlerFactory.cs +++ b/TINKLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandlerFactory.cs @@ -8,6 +8,7 @@ using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using TINK.Model.User; using TINK.MultilingualResources; using Serilog; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock { @@ -19,6 +20,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock /// /// /// + /// Provides info about the smart device (phone, tablet, ...) /// /// View model to be used for progress report and unlocking/ locking view. /// Request handler. @@ -29,6 +31,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock IGeolocation geolocation, ILocksService lockService, Func viewUpdateManager, + ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) @@ -75,6 +78,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -88,6 +92,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -107,6 +112,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -119,6 +125,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -134,6 +141,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -147,6 +155,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -170,6 +179,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -183,6 +193,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -196,6 +207,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); @@ -210,6 +222,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock geolocation, lockService, viewUpdateManager, + smartDevice, viewService, bikesViewModel, activeUser); diff --git a/TINKLib/ViewModel/Bikes/Bike/TariffDescriptionViewModel.cs b/TINKLib/ViewModel/Bikes/Bike/TariffDescriptionViewModel.cs index 0f262dc..297d318 100644 --- a/TINKLib/ViewModel/Bikes/Bike/TariffDescriptionViewModel.cs +++ b/TINKLib/ViewModel/Bikes/Bike/TariffDescriptionViewModel.cs @@ -27,9 +27,13 @@ namespace TINK.ViewModel.Bikes.Bike // No tariff description details available. return string.Empty; +#if USCSHARP9 return string.Format(AppResources.MessageBikesManagementTariffDescriptionTariffHeader, Tariff?.Name ?? "-", Tariff?.Number != null ? Tariff.Number : "-"); +#else + return string.Format(AppResources.MessageBikesManagementTariffDescriptionTariffHeader, Tariff?.Name ?? "-", Tariff?.Number != null ? Tariff.Number.ToString() : "-"); +#endif + } } - } /// /// Costs per hour in euro. diff --git a/TINKLib/ViewModel/Bikes/BikesViewModel.cs b/TINKLib/ViewModel/Bikes/BikesViewModel.cs index a44991f..a12158a 100644 --- a/TINKLib/ViewModel/Bikes/BikesViewModel.cs +++ b/TINKLib/ViewModel/Bikes/BikesViewModel.cs @@ -14,11 +14,16 @@ using TINK.ViewModel.Bikes.Bike; using TINK.ViewModel.Bikes.Bike.BC; using Plugin.Permissions.Abstractions; using Plugin.BLE.Abstractions.Contracts; +using TINK.MultilingualResources; +using TINK.Model.Device; namespace TINK.ViewModel.Bikes { public abstract class BikesViewModel : ObservableCollection, IBikesViewModel { + /// Provides info about the smart device (phone, tablet, ...). + protected ISmartDevice SmartDevice; + /// /// Reference on view servcie to show modal notifications and to perform navigation. /// @@ -73,6 +78,7 @@ namespace TINK.ViewModel.Bikes /// /// /// Mail address of active user. + /// True if report level is verbose, false if not. /// Holds object to query location permisions. /// Holds object to query bluetooth state. /// Specifies on which platform code is run. @@ -81,6 +87,7 @@ namespace TINK.ViewModel.Bikes /// Service to control lock retrieve info. /// Holds whether to poll or not and the periode leght is polling is on. /// Executes actions on GUI thread. + /// Provides info about the smart device (phone, tablet, ...) /// Interface to actuate methodes on GUI. public BikesViewModel( User user, @@ -93,19 +100,20 @@ namespace TINK.ViewModel.Bikes ILocksService lockService, TINK.Settings.PollingParameters polling, Action postAction, + ISmartDevice smartDevice, IViewService viewService, Func itemFactory) { User = user ?? throw new ArgumentException("Can not instantiate bikes page view model- object. No user available."); - + RuntimePlatform = runtimPlatform ?? throw new ArgumentException("Can not instantiate bikes page view model- object. No runtime platform information available."); - Permissions = permissions + PermissionsService = permissions ?? throw new ArgumentException("Can not instantiate bikes page view model- object. No permissions available."); - BluetoothLE = bluetoothLE + BluetoothService = bluetoothLE ?? throw new ArgumentException("Can not instantiate bikes page view model- object. No bluetooth available."); ConnectorFactory = connectorFactory @@ -126,6 +134,9 @@ namespace TINK.ViewModel.Bikes PostAction = postAction ?? throw new ArgumentException("Can not instantiate bikes page view model- object. No post action available."); + SmartDevice = smartDevice + ?? throw new ArgumentException("Can not instantiate bikes page view model- object. No smart device object available."); + ViewService = viewService ?? throw new ArgumentException("Can not instantiate bikes page view model- object. No view available."); @@ -137,7 +148,7 @@ namespace TINK.ViewModel.Bikes m_oPolling = polling; - IsConnected = IsConnectedDelegate(); + isConnected = IsConnectedDelegate(); CollectionChanged += (sender, eventargs) => { @@ -170,6 +181,7 @@ namespace TINK.ViewModel.Bikes LockService, (id) => Remove(id), () => m_oViewUpdateManager, + SmartDevice, ViewService, l_oBike, User, @@ -207,12 +219,24 @@ namespace TINK.ViewModel.Bikes protected User User { get; private set; } +#if USCSHARP9 + public bool IsReportLevelVerbose { get; init; } +#else + public bool IsReportLevelVerbose { get; set; } +#endif + /// Specified whether code is run under iOS or Android. protected string RuntimePlatform { get; private set; } - protected IPermissions Permissions { get; private set; } + /// + /// Service to manage permissions (location) of the app. + /// + protected IPermissions PermissionsService { get; private set; } - protected IBluetoothLE BluetoothLE { get; private set; } + /// + /// Service to manage bluetooth stack. + /// + protected IBluetoothLE BluetoothService { get; private set; } /// /// User which is logged in. @@ -349,12 +373,14 @@ namespace TINK.ViewModel.Bikes if (Exception != null) { // An error occurred getting data from copri. - return Exception.GetShortErrorInfoText(); + return IsReportLevelVerbose + ? Exception.GetShortErrorInfoText() + : AppResources.ActivityTextException; } if (!IsConnected) { - return "Offline."; + return AppResources.ActivityTextConnectionStateOffline; } return ActionText ?? string.Empty; @@ -364,12 +390,12 @@ namespace TINK.ViewModel.Bikes /// /// Removes a bike view model by id. /// - /// Id of bike to removed. - public void Remove(int p_iId) + /// Id of bike to removed. + public void Remove(string id) { foreach (var bike in BikeCollection) { - if (bike.Id == p_iId) + if (bike.Id == id) { BikeCollection.Remove(bike); return; @@ -380,13 +406,13 @@ namespace TINK.ViewModel.Bikes /// /// Gets whether a bike is contained in collection of bikes. /// - /// Id of bike to check existance. + /// Id of bike to check existance. /// True if bike exists. - private bool Contains(int p_iId) + private bool Contains(string id) { foreach (var l_oBike in Items) { - if (l_oBike.Id == p_iId) + if (l_oBike.Id == id) { return true; } diff --git a/TINKLib/ViewModel/BikesAtStation/BikeAtStationInUseStateInfoProvider.cs b/TINKLib/ViewModel/BikesAtStation/BikeAtStationInUseStateInfoProvider.cs index 378206f..bde2000 100644 --- a/TINKLib/ViewModel/BikesAtStation/BikeAtStationInUseStateInfoProvider.cs +++ b/TINKLib/ViewModel/BikesAtStation/BikeAtStationInUseStateInfoProvider.cs @@ -12,7 +12,7 @@ namespace TINK.ViewModel /// Display text public string GetReservedInfo( TimeSpan? remainingTime, - int? station = null, + string station = null, string code = null) { if (remainingTime == null) @@ -40,7 +40,7 @@ namespace TINK.ViewModel /// Display text public string GetBookedInfo( DateTime? from, - int? station = null, + string station = null, string code = null) { if (from == null) diff --git a/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs b/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs index a040bf1..5d3a596 100644 --- a/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs +++ b/TINKLib/ViewModel/BikesAtStation/BikesAtStationPageViewModel.cs @@ -22,6 +22,8 @@ using Plugin.Permissions.Abstractions; using Plugin.BLE.Abstractions.Contracts; using TINK.MultilingualResources; using Plugin.Permissions; +using TINK.Model.Station; +using TINK.Model.Device; namespace TINK.ViewModel.BikesAtStation { @@ -31,14 +33,9 @@ namespace TINK.ViewModel.BikesAtStation public class BikesAtStationPageViewModel : BikesViewModel, INotifyCollectionChanged, INotifyPropertyChanged { /// - /// Reference on view servcie to show modal notifications and to perform navigation. + /// Holds the selected station; /// - private IViewService m_oViewService; - - /// - /// Holds the Id of the selected station; - /// - private readonly int? m_oStation; + private readonly IStation m_oStation; /// Holds a reference to the external trigger service. private Action OpenUrlInExternalBrowser { get; } @@ -47,6 +44,7 @@ namespace TINK.ViewModel.BikesAtStation /// Constructs bike collection view model. /// /// Mail address of active user. + /// True if report level is verbose, false if not. /// Holds object to query location permisions. /// Holds object to query bluetooth state. /// Specifies on which platform code is run. @@ -57,13 +55,14 @@ namespace TINK.ViewModel.BikesAtStation /// All bikes at given station. /// Action to open an external browser. /// Executes actions on GUI thread. + /// Provides info about the smart device (phone, tablet, ...). /// Interface to actuate methodes on GUI. public BikesAtStationPageViewModel( User user, IPermissions permissions, IBluetoothLE bluetoothLE, string runtimPlatform, - int? selectedStation, + IStation selectedStation, Func isConnectedDelegate, Func connectorFactory, IGeolocation geolocation, @@ -71,18 +70,16 @@ namespace TINK.ViewModel.BikesAtStation PollingParameters polling, Action openUrlInExternalBrowser, Action postAction, - IViewService viewService) : base(user, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, viewService, () => new BikeAtStationInUseStateInfoProvider()) + ISmartDevice smartDevice, + IViewService viewService) : base(user, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, smartDevice, viewService, () => new BikeAtStationInUseStateInfoProvider()) { - m_oViewService = viewService - ?? throw new ArgumentException("Can not instantiate bikes at station page view model- object. No view available."); - OpenUrlInExternalBrowser = openUrlInExternalBrowser ?? throw new ArgumentException("Can not instantiate login page view model- object. No user external browse service available."); m_oStation = selectedStation; - Title = string.Format(m_oStation != null - ? string.Format(AppResources.MarkingBikesAtStationTitle, m_oStation.ToString()) + Title = string.Format(m_oStation?.StationName != null + ? m_oStation.StationName : string.Empty); CollectionChanged += (sender, eventargs) => @@ -161,19 +158,32 @@ namespace TINK.ViewModel.BikesAtStation { get { - return new Xamarin.Forms.Command(() => OpenLoginPage()); +#if USEMASTERDETAIL || USEFLYOUT + return new Xamarin.Forms.Command(() => OpenLoginPageAsync()); +#else + return new Xamarin.Forms.Command(async () => await OpenLoginPageAsync()); +#endif } } /// /// Opens login page. /// - public void OpenLoginPage() +#if USEMASTERDETAIL || USEFLYOUT + public void OpenLoginPageAsync() +#else + public async Task OpenLoginPageAsync() +#endif { try { // Switch to map page - m_oViewService.ShowPage(ViewTypes.LoginPage); + +#if USEMASTERDETAIL || USEFLYOUT + ViewService.ShowPage(ViewTypes.LoginPage); +#else + await ViewService.ShowPage("//LoginPage"); +#endif } catch (Exception p_oException) { @@ -188,7 +198,7 @@ namespace TINK.ViewModel.BikesAtStation /// public async Task OnAppearing() { - Log.ForContext().Information($"Bikes at station {m_oStation} is appearing, either due to tap on a station or to app being shown again."); + Log.ForContext().Information($"Bikes at station {m_oStation?.StationName} is appearing, either due to tap on a station or to app being shown again."); ActionText = "Einen Moment bitte..."; @@ -201,15 +211,15 @@ namespace TINK.ViewModel.BikesAtStation Exception = bikesAll.Exception; // Update communication error from query for bikes at station. - var bikesAtStation = bikesAll.Response.GetAtStation(m_oStation); + var bikesAtStation = bikesAll.Response.GetAtStation(m_oStation.Id); var lockIdList = bikesAtStation .GetLockIt() .Cast() .Select(x => x.LockInfo) .ToList(); - Title = string.Format(m_oStation != null - ? m_oStation.ToString() + Title = string.Format(m_oStation?.StationName != null + ? m_oStation.StationName : string.Empty); if (LockService is ILocksServiceFake serviceFake) @@ -223,14 +233,14 @@ namespace TINK.ViewModel.BikesAtStation if (bikesAtStation.GetLockIt().Count > 0 && RuntimePlatform == Device.Android) { - var status = await Permissions.CheckPermissionStatusAsync(); + var status = await PermissionsService.CheckPermissionStatusAsync(); if (status != PermissionStatus.Granted) { - var permissionResult = await Permissions.RequestPermissionAsync(); + var permissionResult = await PermissionsService.RequestPermissionAsync(); if (permissionResult != PermissionStatus.Granted) { - var dialogResult = await m_oViewService.DisplayAlert( + var dialogResult = await ViewService.DisplayAlert( AppResources.MessageTitleHint, AppResources.MessageBikesManagementLocationPermissionOpenDialog, AppResources.MessageAnswerYes, @@ -249,13 +259,13 @@ namespace TINK.ViewModel.BikesAtStation } // Open permissions dialog. - Permissions.OpenAppSettings(); + PermissionsService.OpenAppSettings(); } } if (Geolocation.IsGeolcationEnabled == false) { - await m_oViewService.DisplayAlert( + await ViewService.DisplayAlert( AppResources.MessageTitleHint, AppResources.MessageBikesManagementLocationActivation, AppResources.MessageAnswerOk); @@ -270,9 +280,9 @@ namespace TINK.ViewModel.BikesAtStation } // Check if bluetooth is activated. - if (await BluetoothLE.GetBluetoothState() != BluetoothState.On) + if (await BluetoothService.GetBluetoothState() != BluetoothState.On) { - await m_oViewService.DisplayAlert( + await ViewService.DisplayAlert( AppResources.MessageTitleHint, AppResources.MessageBikesManagementBluetoothActivation, AppResources.MessageAnswerOk); @@ -319,14 +329,14 @@ namespace TINK.ViewModel.BikesAtStation PostAction( unused => { - ActionText = "Aktualisiere..."; + ActionText = AppResources.ActivityTextUpdating; IsConnected = IsConnectedDelegate(); }, null); var result = ConnectorFactory(IsConnected).Query.GetBikesAsync().Result; - BikeCollection bikes = result.Response.GetAtStation(m_oStation); + BikeCollection bikes = result.Response.GetAtStation(m_oStation.Id); var exception = result.Exception; if (exception != null) diff --git a/TINK/TINK/ViewModel/FeesAndBikes/HelpContactViewModel.cs b/TINKLib/ViewModel/FeesAndBikes/HelpContactViewModel.cs similarity index 70% rename from TINK/TINK/ViewModel/FeesAndBikes/HelpContactViewModel.cs rename to TINKLib/ViewModel/FeesAndBikes/HelpContactViewModel.cs index 5725b43..5d1b942 100644 --- a/TINK/TINK/ViewModel/FeesAndBikes/HelpContactViewModel.cs +++ b/TINKLib/ViewModel/FeesAndBikes/HelpContactViewModel.cs @@ -1,24 +1,34 @@ using System.ComponentModel; using Xamarin.Forms; using TINK.Services.CopriApi.ServerUris; -using TINK.Model.User.Account; +using System; + namespace TINK.ViewModel.Contact { public class HelpContactViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; + /// Gets the platfrom specific prefix. + private Func ResourceProvider { get; set; } + /// Holds value wether site caching is on or off. bool IsSiteCachingOn { get; } /// Constructs view model. /// Set of user permissions + /// Delegate to get an an embedded html ressource. Used as fallback if download from web page does not work and cache is empty. public HelpContactViewModel( string hostName, - bool isSiteCachingOn) + bool isSiteCachingOn, + Func resourceProvider) { HostName = hostName; IsSiteCachingOn = isSiteCachingOn; + + ResourceProvider = resourceProvider + ?? throw new ArgumentException($"Can not instantiate {typeof(HelpContactViewModel)}-object. No ressource provider availalbe."); + } /// Holds the name of the host. @@ -30,14 +40,14 @@ namespace TINK.ViewModel.Contact RentBikeText = new HtmlWebViewSource { Html = HostName.GetIsCopri() - ? ViewModelResourceHelper.GetSource("HtmlResouces.V02.InfoRentBike.html") - : await ViewModelHelper.GetSource($"https://{HostName}/{CopriHelper.SHAREE_SILTEFOLDERNAME}/tariff_info.html", IsSiteCachingOn) + ? ResourceProvider("HtmlResouces.V02.InfoRentBike.html") + : await ViewModelHelper.GetSource($"https://{HostName}/{CopriHelper.SHAREE_SILTEFOLDERNAME}/tariff_info_1.html", IsSiteCachingOn) }; TypesOfBikesText = new HtmlWebViewSource { Html = HostName.GetIsCopri() - ? ViewModelResourceHelper.GetSource("HtmlResouces.V02.InfoTypesOfBikes.html") + ? ResourceProvider("HtmlResouces.V02.InfoTypesOfBikes.html") : await ViewModelHelper.GetSource($"https://{HostName}/{CopriHelper.SHAREE_SILTEFOLDERNAME}/bike_info.html", IsSiteCachingOn) }; } diff --git a/TINKLib/ViewModel/IInUseStateInfoProvider.cs b/TINKLib/ViewModel/IInUseStateInfoProvider.cs index 2bf213e..814c22f 100644 --- a/TINKLib/ViewModel/IInUseStateInfoProvider.cs +++ b/TINKLib/ViewModel/IInUseStateInfoProvider.cs @@ -10,7 +10,7 @@ namespace TINK.ViewModel /// Display text string GetReservedInfo( TimeSpan? p_oRemainingTime, - int? p_strStation = null, + string p_strStation = null, string p_strCode = null); /// @@ -19,7 +19,7 @@ namespace TINK.ViewModel /// Display text string GetBookedInfo( DateTime? p_oFrom, - int? p_strStation = null, + string p_strStation = null, string p_strCode = null); } } diff --git a/TINKLib/ViewModel/Info/BikeInfo/BikeInfoCarouselViewModel.cs b/TINKLib/ViewModel/Info/BikeInfo/BikeInfoCarouselViewModel.cs index 4308a8f..12844ee 100644 --- a/TINKLib/ViewModel/Info/BikeInfo/BikeInfoCarouselViewModel.cs +++ b/TINKLib/ViewModel/Info/BikeInfo/BikeInfoCarouselViewModel.cs @@ -140,16 +140,16 @@ namespace TINK.ViewModel.Info.BikeInfo /// Gets the carousel page items public IList CarouselItems { get; } - + /// /// Commang object to bind login button to view model. /// +#if USEMASTERDETAIL || USEFLYOUT private Action CloseAction - { - get - { - return () => m_oViewService.ShowPage(ViewTypes.MapPage); - } - } + => () => m_oViewService.ShowPage(ViewTypes.MapPage); +#else + private Action CloseAction + => async () => await m_oViewService.ShowPage("//MapPage"); +#endif } } diff --git a/TINKLib/ViewModel/Login/LoginPageViewModel.cs b/TINKLib/ViewModel/Login/LoginPageViewModel.cs index 735664e..c4da732 100644 --- a/TINKLib/ViewModel/Login/LoginPageViewModel.cs +++ b/TINKLib/ViewModel/Login/LoginPageViewModel.cs @@ -6,7 +6,7 @@ using TINK.Model.User.Account; using System.ComponentModel; using System; using System.Threading.Tasks; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using Serilog; using TINK.ViewModel.Map; using Plugin.Connectivity; @@ -14,6 +14,7 @@ using TINK.Model; using System.Linq; using System.Collections.Generic; using TINK.MultilingualResources; +using TINK.ViewModel.Info; namespace TINK.ViewModel { @@ -22,7 +23,7 @@ namespace TINK.ViewModel /// /// Reference on view servcie to show modal notifications and to perform navigation. /// - private IViewService m_oViewService; + private readonly IViewService m_oViewService; #if BACKSTYLE /// Reference to naviagion object to navigate back to map page when login succeeded. @@ -168,7 +169,7 @@ namespace TINK.ViewModel { get { - return new Command(async () => await TryLogin()); + return new Command(async () => await Login()); } } @@ -208,9 +209,9 @@ namespace TINK.ViewModel /// User request to log in. /// #if BACKSTYLE - public async void TryLogin() + public async void Login() #else - public async Task TryLogin() + public async Task Login() #endif { try @@ -224,7 +225,7 @@ namespace TINK.ViewModel // Do login. var l_oAccount = await TinkApp.GetConnector(CrossConnectivity.Current.IsConnected).Command.DoLogin(MailAddress, Password, TinkApp.ActiveUser.DeviceId); - TinkApp.ActiveUser.Login(l_oAccount); + await TinkApp.ActiveUser.Login(l_oAccount); // Update map page filter because user might be of both groups TINK an Konrad. TinkApp.GroupFilterMapPage = @@ -252,6 +253,15 @@ namespace TINK.ViewModel return; } + catch (UnsupportedCopriVersionDetectedException) + { + await m_oViewService.DisplayAlert( + AppResources.MessageLoginErrorTitle, + string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), + AppResources.MessageAnswerOk); + + return; + } catch (Exception l_oException) { // Copri server is not reachable. @@ -307,12 +317,20 @@ namespace TINK.ViewModel if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.FILTERTINKGENERAL)) { // No need to show "Anleitung TINK Räder" because user can not use tink. +#if USEMASTERDETAIL || USEFLYOUT m_oViewService.ShowPage(ViewTypes.MapPage); +#else + await m_oViewService.ShowPage("//MapPage"); +#endif return; } // Swich to map page +#if USEMASTERDETAIL || USEFLYOUT m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions); +#else + await m_oViewService.ShowPage("//MapPage"); +#endif } catch (Exception p_oException) { diff --git a/TINKLib/ViewModel/Map/MapPageViewModel.cs b/TINKLib/ViewModel/Map/MapPageViewModel.cs index d721d25..a131a14 100644 --- a/TINKLib/ViewModel/Map/MapPageViewModel.cs +++ b/TINKLib/ViewModel/Map/MapPageViewModel.cs @@ -4,7 +4,7 @@ using TINK.Model.Station; using System; using System.Linq; using TINK.Model.Bike; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model; using Serilog; using System.Collections.Generic; @@ -12,7 +12,9 @@ using System.Threading.Tasks; using System.ComponentModel; using Xamarin.Forms.GoogleMaps; using System.Collections.ObjectModel; +#if USEMASTERDETAIL || USEFLYOUT using TINK.View.MasterDetail; +#endif using TINK.Settings; using TINK.Model.Connector; using TINK.Model.Services.CopriApi; @@ -24,6 +26,9 @@ using TINK.MultilingualResources; using TINK.Services.BluetoothLock; using TINK.Model.Services.CopriApi.ServerUris; using TINK.ViewModel.Info; +using TINK.Repository; +using Plugin.Permissions.Abstractions; +using TINK.Model.Services.Geolocation; #if !TRYNOTBACKSTYLE #endif @@ -43,6 +48,18 @@ namespace TINK.ViewModel.Map /// private Exception m_oException; + + /// + /// Service to query/ manage permissions (location) of the app. + /// + private IPermissions PermissionsService { get; } + + /// + /// Service to manage bluetooth stack. + /// + private Plugin.BLE.Abstractions.Contracts.IBluetoothLE BluetoothService { get; set; } + + /// Notifies view about changes. public event PropertyChangedEventHandler PropertyChanged; @@ -58,9 +75,10 @@ namespace TINK.ViewModel.Map /// Delegate to perform navigation. private INavigation m_oNavigation; +#if USEMASTERDETAIL || USEFLYOUT /// Delegate to perform navigation. private INavigationMasterDetail m_oNavigationMasterDetail; - +#endif private ObservableCollection pins; public ObservableCollection Pins @@ -81,7 +99,9 @@ namespace TINK.ViewModel.Map /// False if user tabed on station marker to show bikes at a given station. private bool isMapPageEnabled = false; - + + Model.Services.Geolocation.IGeolocation GeolocationService { get; } + /// False if user tabed on station marker to show bikes at a given station. public bool IsMapPageEnabled { get => isMapPageEnabled; @@ -97,30 +117,44 @@ namespace TINK.ViewModel.Map /// Prevents an invalid instane to be created. /// Reference to tink app model. - /// Delegate to center map and set zoom level. - /// View service to notify user. - /// Interface to navigate. + /// Delegate to center map and set zoom level. + /// View service to notify user. + /// Interface to navigate. public MapPageViewModel( ITinkApp tinkApp, - Action p_oMoveToRegionDelegate, - IViewService p_oViewService, - INavigation p_oNavigation) + IPermissions permissionsService, + Plugin.BLE.Abstractions.Contracts.IBluetoothLE bluetoothService, + IGeolocation geolocationService, + Action moveToRegionDelegate, + IViewService viewService, + INavigation navigation) { TinkApp = tinkApp ?? throw new ArgumentException("Can not instantiate map page view model- object. No tink app object available."); - m_oMoveToRegionDelegate = p_oMoveToRegionDelegate + PermissionsService = permissionsService ?? + throw new ArgumentException($"Can not instantiate {nameof(MapPageViewModel)}. Permissions service object must never be null."); + + BluetoothService = bluetoothService ?? + throw new ArgumentException($"Can not instantiate {nameof(MapPageViewModel)}. Bluetooth service object must never be null."); + + GeolocationService = geolocationService ?? + throw new ArgumentException($"Can not instantiate {nameof(MapPageViewModel)}. Geolocation service object must never be null."); + + m_oMoveToRegionDelegate = moveToRegionDelegate ?? throw new ArgumentException("Can not instantiate map page view model- object. No move delegate available."); - m_oViewService = p_oViewService + m_oViewService = viewService ?? throw new ArgumentException("Can not instantiate map page view model- object. No view available."); - m_oNavigation = p_oNavigation + m_oNavigation = navigation ?? throw new ArgumentException("Can not instantiate map page view model- object. No navigation service available."); m_oViewUpdateManager = new IdlePollingUpdateTaskManager(); +#if USEMASTERDETAIL || USEFLYOUT m_oNavigationMasterDetail = new EmptyNavigationMasterDetail(); +#endif Polling = PollingParameters.NoPolling; @@ -143,42 +177,46 @@ namespace TINK.ViewModel.Map } } +#if USEMASTERDETAIL || USEFLYOUT /// Delegate to perform navigation. public INavigationMasterDetail NavigationMasterDetail { set { m_oNavigationMasterDetail = value; } } +#endif public Command PinClickedCommand => new Command( args => { - OnStationClicked(int.Parse(args.Pin.Tag.ToString())); + OnStationClicked(args.Pin.Tag.ToString()); args.Handled = true; // Prevents map to be centered to selected pin. }); /// /// One time setup: Sets pins into map and connects to events. /// - private void InitializePins(StationDictionary p_oStations) + private void InitializePins(StationDictionary stations) { // Add pins to stations. - Log.ForContext().Debug($"Request to draw {p_oStations.Count} pins."); - foreach (var l_oStation in p_oStations) + Log.ForContext().Debug($"Request to draw {stations.Count} pins."); + foreach (var station in stations) { - if (l_oStation.Position == null) + if (station.Position == null) { // There should be no reason for a position object to be null but this alreay occurred in past. - Log.ForContext().Error("Postion object of station {@l_oStation} is null.", l_oStation); + Log.ForContext().Error("Postion object of station {@l_oStation} is null.", station); continue; } var l_oPin = new Pin { - Position = new Xamarin.Forms.GoogleMaps.Position(l_oStation.Position.Latitude, l_oStation.Position.Longitude), - Label = l_oStation.Id > CUSTOM_ICONS_COUNT - ? l_oStation.GetStationName() + + Position = new Xamarin.Forms.GoogleMaps.Position(station.Position.Latitude, station.Position.Longitude), + Label = long.TryParse(station.Id, out long stationId) && stationId > CUSTOM_ICONS_COUNT + ? station.GetStationName() : string.Empty, // Stations with custom icons have already a id marker. No need for a label. - Tag = l_oStation.Id, + + Tag = station.Id, IsVisible = false, // Set to false to prevent showing default icons (flickering). }; @@ -187,41 +225,41 @@ namespace TINK.ViewModel.Map } /// Update all stations from TINK. - /// List of colors to apply. - private void UpdatePinsColor(IList p_oStationsColorList) + /// List of colors to apply. + private void UpdatePinsColor(IList stationsColorList) { - Log.ForContext().Debug($"Starting update of stations pins color for {p_oStationsColorList.Count} stations..."); + Log.ForContext().Debug($"Starting update of stations pins color for {stationsColorList.Count} stations..."); // Update colors of pins. - for (int l_iPinIndex = 0; l_iPinIndex < p_oStationsColorList.Count; l_iPinIndex++) + for (int pinIndex = 0; pinIndex < stationsColorList.Count; pinIndex++) { - var l_iStationId = int.Parse(Pins[l_iPinIndex].Tag.ToString()); - var indexPartPrefix = l_iStationId <= CUSTOM_ICONS_COUNT - ? $"{l_iStationId}" // there is a station marker with index letter for given station id + var indexPartPrefix = int.TryParse(Pins[pinIndex].Tag.ToString(), out int stationId) + && stationId <= CUSTOM_ICONS_COUNT + ? $"{stationId}" // there is a station marker with index letter for given station id : "Open"; // there is no station marker. Use open marker. - var colorPartPrefix = GetRessourceNameColorPart(p_oStationsColorList[l_iPinIndex]); + var colorPartPrefix = GetRessourceNameColorPart(stationsColorList[pinIndex]); var l_iName = $"{indexPartPrefix.ToString().PadLeft(2, '0')}_{colorPartPrefix}{(DeviceInfo.Platform == DevicePlatform.Android ? ".png" : string.Empty)}"; try { - Pins[l_iPinIndex].Icon = BitmapDescriptorFactory.FromBundle(l_iName); + Pins[pinIndex].Icon = BitmapDescriptorFactory.FromBundle(l_iName); } catch (Exception l_oException) { Log.ForContext().Error("Station icon {l_strName} can not be loaded. {@l_oException}.", l_oException); - Pins[l_iPinIndex].Label = l_iStationId.ToString(); - Pins[l_iPinIndex].Icon = BitmapDescriptorFactory.DefaultMarker(p_oStationsColorList[l_iPinIndex]); + Pins[pinIndex].Label = stationId.ToString(); + Pins[pinIndex].Icon = BitmapDescriptorFactory.DefaultMarker(stationsColorList[pinIndex]); } - Pins[l_iPinIndex].IsVisible = true; + Pins[pinIndex].IsVisible = true; } var pinsCount = Pins.Count; - for (int pinIndex = p_oStationsColorList.Count; pinIndex < pinsCount; pinIndex++) + for (int pinIndex = stationsColorList.Count; pinIndex < pinsCount; pinIndex++) { - Log.ForContext().Error($"Unexpected count of pins detected. Expected {p_oStationsColorList.Count} but is {pinsCount}."); + Log.ForContext().Error($"Unexpected count of pins detected. Expected {stationsColorList.Count} but is {pinsCount}."); Pins[pinIndex].IsVisible = false; } @@ -229,31 +267,31 @@ namespace TINK.ViewModel.Map } /// Gets the color related part of the ressrouce name. - /// Color to get name for. + /// Color to get name for. /// Resource name. - private static string GetRessourceNameColorPart(Color p_oColor) + private static string GetRessourceNameColorPart(Color color) { - if (p_oColor == Color.Blue) + if (color == Color.Blue) { return "Blue"; } - if (p_oColor == Color.Green) + if (color == Color.Green) { return "Green"; } - if (p_oColor == Color.LightBlue) + if (color == Color.LightBlue) { return "LightBlue"; } - if (p_oColor == Color.Red) + if (color == Color.Red) { return "Red"; } - return p_oColor.ToString(); + return color.ToString(); } /// @@ -283,13 +321,12 @@ namespace TINK.ViewModel.Map ActionText = AppResources.ActivityTextMyBikesLoadingBikes; // Check location permission - var _permissions = TinkApp.Permissions; - var status = await _permissions.CheckPermissionStatusAsync(); + var status = await PermissionsService.CheckPermissionStatusAsync(); if (TinkApp.CenterMapToCurrentLocation - && !TinkApp.GeolocationServices.Active.IsSimulation + && !GeolocationService.IsSimulation && status != Plugin.Permissions.Abstractions.PermissionStatus.Granted) { - var permissionResult = await _permissions.RequestPermissionAsync(); + var permissionResult = await PermissionsService.RequestPermissionAsync(); if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted) { @@ -302,7 +339,7 @@ namespace TINK.ViewModel.Map if (dialogResult) { // User decided to give access to locations permissions. - _permissions.OpenAppSettings(); + PermissionsService.OpenAppSettings(); ActionText = ""; IsRunning = false; IsMapPageEnabled = true; @@ -317,7 +354,7 @@ namespace TINK.ViewModel.Map try { currentLocation = TinkApp.CenterMapToCurrentLocation - ? await TinkApp.GeolocationServices.Active.GetAsync() + ? await GeolocationService.GetAsync() : null; } catch (Exception ex) @@ -332,6 +369,16 @@ namespace TINK.ViewModel.Map IsConnected = TinkApp.GetIsConnected(); var resultStationsAndBikes = await TinkApp.GetConnector(IsConnected).Query.GetBikesAndStationsAsync(); + TinkApp.Stations = resultStationsAndBikes.Response.StationsAll; + + if (Pins.Count > 0 && Pins.Count != resultStationsAndBikes.Response.StationsAll.Count) + { + // Either + // - user logged in/ logged out which might lead to more/ less stations beeing available + // - new stations were added/ existing ones remove + Pins.Clear(); + } + // Check if there are alreay any pins to the map // i.e detecte first call of member OnAppearing after construction if (Pins.Count <= 0) @@ -341,13 +388,23 @@ namespace TINK.ViewModel.Map // Map was not yet initialized. // Get stations from Copri Log.ForContext().Verbose("No pins detected on page."); - if (resultStationsAndBikes.Response.StationsAll.CopriVersion >= new Version(4, 1)) + if (resultStationsAndBikes.Response.StationsAll.CopriVersion < CopriCallsStatic.UnsupportedVersionLower) { await m_oViewService.DisplayAlert( - "Warnung", - string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), - "OK"); - + AppResources.MessageWaring, + string.Format(AppResources.MessageCopriVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), + AppResources.MessageAnswerOk); + + Log.ForContext().Error($"Outdated version of app detected. Version expected is {resultStationsAndBikes.Response.StationsAll.CopriVersion}."); + } + + if (resultStationsAndBikes.Response.StationsAll.CopriVersion >= CopriCallsStatic.UnsupportedVersionUpper) + { + await m_oViewService.DisplayAlert( + AppResources.MessageWaring, + string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), + AppResources.MessageAnswerOk); + Log.ForContext().Error($"Outdated version of app detected. Version expected is {resultStationsAndBikes.Response.StationsAll.CopriVersion}."); } @@ -360,7 +417,7 @@ namespace TINK.ViewModel.Map if (resultStationsAndBikes.Exception?.GetType() == typeof(AuthcookieNotDefinedException)) { - Log.ForContext().Error("Map page is shown (probable for the first time after startup of app) and COPRI copri an auth cookie not defined error.{@l_oException}", resultStationsAndBikes.Exception); + Log.ForContext().Error("Map page is shown (probable for the first time after startup of app) and COPRI auth cookie is not defined. {@l_oException}", resultStationsAndBikes.Exception); // COPRI reports an auth cookie error. await m_oViewService.DisplayAlert( @@ -375,12 +432,12 @@ namespace TINK.ViewModel.Map // Update pin colors. Log.ForContext().Verbose("Starting update pins color..."); - var l_oColors = GetStationColors( + var colors = GetStationColors( Pins.Select(x => x.Tag.ToString()).ToList(), resultStationsAndBikes.Response.Bikes); // Update pins color form count of bikes located at station. - UpdatePinsColor(l_oColors); + UpdatePinsColor(colors); m_oViewUpdateManager = CreateUpdateTask(); @@ -476,7 +533,7 @@ namespace TINK.ViewModel.Map TinkApp.PostAction( unused => { - ActionText = "Aktualisiere..."; + ActionText = AppResources.ActivityTextUpdating; IsConnected = TinkApp.GetIsConnected(); }, null); @@ -546,7 +603,7 @@ namespace TINK.ViewModel.Map /// User clicked on a bike. /// Id of station user clicked on. - public async void OnStationClicked(int selectedStationId) + public async void OnStationClicked(string selectedStationId) { try { @@ -555,7 +612,8 @@ namespace TINK.ViewModel.Map // Lock action to prevent multiple instances of "BikeAtStation" being opened. IsMapPageEnabled = false; - TinkApp.SelectedStation = selectedStationId; + TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId) + ?? new Station(selectedStationId, new List(), null); // Station might not be in list StationDictinaly because this list is not updatd in background task. #if TRYNOTBACKSTYLE m_oNavigation.ShowPage( @@ -585,13 +643,13 @@ namespace TINK.ViewModel.Map /// /// Gets the list of station color for all stations. /// - /// Station id list to get color for. + /// Station id list to get color for. /// private static IList GetStationColors( - IEnumerable p_oStationsId, + IEnumerable stationsId, BikeCollection bikesAll) { - if (p_oStationsId == null) + if (stationsId == null) { Log.ForContext().Debug("No stations available to update color for."); return new List(); @@ -601,41 +659,33 @@ namespace TINK.ViewModel.Map { // If object is null an error occurred querrying bikes availalbe or bikes occpied which results in an unknown state. Log.ForContext().Error("No bikes available to determine pins color."); - return new List(p_oStationsId.Select(x => Color.Blue)); + return new List(stationsId.Select(x => Color.Blue)); } // Get state for each station. - var l_oColors = new List(); - foreach (var l_strStationId in p_oStationsId) + var colors = new List(); + foreach (var stationId in stationsId) { - if (int.TryParse(l_strStationId, out int l_iStationId) == false) - { - // Station id is not valid. - Log.ForContext().Error($"A station id {l_strStationId} is invalid (not integer)."); - l_oColors.Add(Color.Blue); - continue; - } - // Get color of given station. - var l_oBikesAtStation = bikesAll.Where(x => x.CurrentStation == l_iStationId); - if (l_oBikesAtStation.FirstOrDefault(x => x.State.Value != Model.State.InUseStateEnum.Disposable) != null) + var bikesAtStation = bikesAll.Where(x => x.CurrentStation == stationId).ToList(); + if (bikesAtStation.FirstOrDefault(x => x.State.Value != Model.State.InUseStateEnum.Disposable) != null) { // There is at least one requested or booked bike - l_oColors.Add(Color.LightBlue); + colors.Add(Color.LightBlue); continue; } - if (l_oBikesAtStation.ToList().Count > 0) + if (bikesAtStation.ToList().Count > 0) { // There is at least one bike available - l_oColors.Add(Color.Green); + colors.Add(Color.Green); continue; } - l_oColors.Add(Color.Red); + colors.Add(Color.Red); } - return l_oColors; + return colors; } /// @@ -733,12 +783,14 @@ namespace TINK.ViewModel.Map if (Exception != null) { // An error occurred getting data from copri. - return Exception.GetShortErrorInfoText(); + return TinkApp.IsReportLevelVerbose + ? Exception.GetShortErrorInfoText() + : AppResources.ActivityTextException; } if (!IsConnected) { - return "Offline."; + return AppResources.ActivityTextConnectionStateOffline; } return ActionText ?? string.Empty; @@ -806,13 +858,12 @@ namespace TINK.ViewModel.Map Pins.Clear(); // Check location permission - var _permissions = TinkApp.Permissions; - var status = await _permissions.CheckPermissionStatusAsync(); + var status = await PermissionsService.CheckPermissionStatusAsync(); if (TinkApp.CenterMapToCurrentLocation - && !TinkApp.GeolocationServices.Active.IsSimulation + && !GeolocationService.IsSimulation && status != Plugin.Permissions.Abstractions.PermissionStatus.Granted) { - var permissionResult = await _permissions.RequestPermissionAsync(); + var permissionResult = await PermissionsService.RequestPermissionAsync(); if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted) { @@ -825,7 +876,7 @@ namespace TINK.ViewModel.Map if (dialogResult) { // User decided to give access to locations permissions. - _permissions.OpenAppSettings(); + PermissionsService.OpenAppSettings(); IsMapPageEnabled = true; ActionText = ""; return; @@ -835,7 +886,7 @@ namespace TINK.ViewModel.Map // Do not use property .State to get bluetooth state due // to issue https://hausource.visualstudio.com/TINK/_workitems/edit/116 / // see https://github.com/xabre/xamarin-bluetooth-le/issues/112#issuecomment-380994887 - if (await CrossBluetoothLE.Current.GetBluetoothState() != Plugin.BLE.Abstractions.Contracts.BluetoothState.On) + if (await BluetoothService.GetBluetoothState() != Plugin.BLE.Abstractions.Contracts.BluetoothState.On) { await m_oViewService.DisplayAlert( AppResources.MessageTitleHint, @@ -852,7 +903,7 @@ namespace TINK.ViewModel.Map try { currentLocation = TinkApp.CenterMapToCurrentLocation - ? await TinkApp.GeolocationServices.Active.GetAsync() + ? await GeolocationService.GetAsync() : null; } catch (Exception ex) diff --git a/TINKLib/ViewModel/MyBikes/MyBikeViewModel.cs b/TINKLib/ViewModel/MyBikes/MyBikeViewModel.cs index d9adff0..1032b62 100644 --- a/TINKLib/ViewModel/MyBikes/MyBikeViewModel.cs +++ b/TINKLib/ViewModel/MyBikes/MyBikeViewModel.cs @@ -12,7 +12,7 @@ namespace TINK.ViewModel /// Display text public string GetReservedInfo( TimeSpan? remainingTime, - int? stationId = null, + string stationId = null, string code = null) { if (remainingTime == null) @@ -38,20 +38,20 @@ namespace TINK.ViewModel return string.Format(AppResources.StatusTextReservationExpiredCodeLocationMaxReservationTime, code, stationId, StateRequestedInfo.MaximumReserveTime.Minutes); } - if (stationId.HasValue) + if (!string.IsNullOrEmpty(stationId)) { if (!string.IsNullOrEmpty(code)) { return string.Format( AppResources.StatusTextReservationExpiredCodeLocationReservationTime, code, - ViewModelHelper.GetStationName(stationId.Value), + ViewModelHelper.GetStationName(stationId), remainingTime.Value.Minutes); } return string.Format( AppResources.StatusTextReservationExpiredLocationReservationTime, - ViewModelHelper.GetStationName(stationId.Value), + ViewModelHelper.GetStationName(stationId), remainingTime.Value.Minutes); } @@ -67,7 +67,7 @@ namespace TINK.ViewModel /// Display text public string GetBookedInfo( DateTime? from, - int? stationId = null, + string stationId = null, string code = null) { if (from == null) @@ -77,12 +77,12 @@ namespace TINK.ViewModel if (!string.IsNullOrEmpty(code)) { - if(stationId.HasValue) + if(!string.IsNullOrEmpty(stationId)) { return string.Format( AppResources.StatusTextBookedCodeLocationSince, code, - ViewModelHelper.GetStationName(stationId.Value), + ViewModelHelper.GetStationName(stationId), from.Value.ToString(BikeViewModelBase.TIMEFORMAT)); } diff --git a/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs b/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs index 37657b9..7eb01ff 100644 --- a/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs +++ b/TINKLib/ViewModel/MyBikes/MyBikesPageViewModel.cs @@ -22,6 +22,7 @@ using Plugin.Permissions; using Plugin.Permissions.Abstractions; using Plugin.BLE.Abstractions.Contracts; using TINK.MultilingualResources; +using TINK.Model.Device; namespace TINK.ViewModel.MyBikes { @@ -31,6 +32,7 @@ namespace TINK.ViewModel.MyBikes /// Constructs bike collection view model in case information about occupied bikes is available. /// /// Mail address of active user. + /// True if report level is verbose, false if not. /// Holds object to query location permisions. /// Holds object to query bluetooth state. /// Specifies on which platform code is run. @@ -39,6 +41,7 @@ namespace TINK.ViewModel.MyBikes /// Service to control lock retrieve info. /// Holds whether to poll or not and the periode leght is polling is on. /// Executes actions on GUI thread. + /// Provides info about the smart device (phone, tablet, ...). /// Interface to actuate methodes on GUI. public MyBikesPageViewModel( User p_oUser, @@ -51,7 +54,8 @@ namespace TINK.ViewModel.MyBikes ILocksService lockService, PollingParameters p_oPolling, Action postAction, - IViewService viewService) : base(p_oUser, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, p_oPolling, postAction, viewService, () => new MyBikeInUseStateInfoProvider()) + ISmartDevice smartDevice, + IViewService viewService) : base(p_oUser, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, p_oPolling, postAction, smartDevice, viewService, () => new MyBikeInUseStateInfoProvider()) { CollectionChanged += (sender, eventargs) => { @@ -115,10 +119,10 @@ namespace TINK.ViewModel.MyBikes && RuntimePlatform == Device.Android) { // Check location permission - var status = await Permissions.CheckPermissionStatusAsync(); + var status = await PermissionsService.CheckPermissionStatusAsync(); if (status != PermissionStatus.Granted) { - var permissionResult = await Permissions.RequestPermissionAsync(); + var permissionResult = await PermissionsService.RequestPermissionAsync(); if (permissionResult != PermissionStatus.Granted) { @@ -141,7 +145,7 @@ namespace TINK.ViewModel.MyBikes } // Open permissions dialog. - Permissions.OpenAppSettings(); + PermissionsService.OpenAppSettings(); } } @@ -163,7 +167,7 @@ namespace TINK.ViewModel.MyBikes } // Bluetooth state - if (await BluetoothLE.GetBluetoothState() != BluetoothState.On) + if (await BluetoothService.GetBluetoothState() != BluetoothState.On) { await ViewService.DisplayAlert( AppResources.MessageTitleHint, @@ -230,7 +234,7 @@ namespace TINK.ViewModel.MyBikes PostAction( unused => { - ActionText = "Aktualisiere..."; + ActionText = AppResources.ActivityTextUpdating; IsConnected = IsConnectedDelegate(); }, null); diff --git a/TINKLib/ViewModel/PollingUpdateTask.cs b/TINKLib/ViewModel/PollingUpdateTask.cs index 10aa360..f82f178 100644 --- a/TINKLib/ViewModel/PollingUpdateTask.cs +++ b/TINKLib/ViewModel/PollingUpdateTask.cs @@ -3,8 +3,7 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using TINK.Model.Repository.Exception; -using TINK.Model.Repository.Request; +using TINK.Repository.Exception; namespace TINK.ViewModel { diff --git a/TINKLib/ViewModel/Settings/SettingsPageViewModel.cs b/TINKLib/ViewModel/Settings/SettingsPageViewModel.cs index 16b39c9..420a142 100644 --- a/TINKLib/ViewModel/Settings/SettingsPageViewModel.cs +++ b/TINKLib/ViewModel/Settings/SettingsPageViewModel.cs @@ -6,7 +6,7 @@ using System.ComponentModel; using System.Threading.Tasks; using TINK.Model; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Services.Geolocation; using TINK.Settings; using TINK.View; @@ -16,6 +16,7 @@ using System.Linq; using TINK.Model.User.Account; using TINK.Services.BluetoothLock; using Xamarin.Forms; +using TINK.Services; namespace TINK.ViewModel { @@ -51,12 +52,17 @@ namespace TINK.ViewModel /// private LogEventLevel m_oMinimumLogEventLevel; + /// Gets a value indicating whether reporting level is verbose or not. + public bool IsReportLevelVerbose { get; set; } + /// List of copri server uris. public ServicesViewModel Themes { get; } /// Reference on the tink app instance. private ITinkApp TinkApp { get; } + IServicesContainer GeoloctionServicesContainer { get; } + /// Constructs a settings page view model object. /// Reference to tink app model. /// @@ -69,16 +75,22 @@ namespace TINK.ViewModel /// Interface to view public SettingsPageViewModel( ITinkApp tinkApp, + IServicesContainer geoloctionServicesContainer, IViewService p_oViewService) { TinkApp = tinkApp ?? throw new ArgumentException("Can not instantiate settings page view model- object. No tink app object available."); + GeoloctionServicesContainer = geoloctionServicesContainer + ?? throw new ArgumentException($"Can not instantiate {nameof(SettingsPageViewModel)}- object. Geolocation services container object must not be null."); + m_oViewService = p_oViewService ?? throw new ArgumentException("Can not instantiate settings page view model- object. No user view service available."); m_oMinimumLogEventLevel = TinkApp.MinimumLogEventLevel; + IsReportLevelVerbose = TinkApp.IsReportLevelVerbose; + CenterMapToCurrentLocation = TinkApp.CenterMapToCurrentLocation; ExternalFolder = TinkApp.ExternalFolder; @@ -140,12 +152,12 @@ namespace TINK.ViewModel TinkApp.LocksServices.Active.GetType().FullName)); GeolocationServices = new ServicesViewModel( - TinkApp.GeolocationServices.Select(x => x.GetType().FullName), + GeoloctionServicesContainer.Select(x => x.GetType().FullName), new Dictionary { { typeof(LastKnownGeolocationService).FullName, "Smartdevice-LastKnowGeolocation" }, { typeof(GeolocationService).FullName, "Smartdevice-MediumAccuracy" }, { typeof(SimulatedGeolocationService).FullName, "Simulation-AlwaysSamePosition" } }, - TinkApp.GeolocationServices.Active.GetType().FullName); + GeoloctionServicesContainer.Active.GetType().FullName); } /// @@ -212,7 +224,7 @@ namespace TINK.ViewModel /// public string DeviceIdentifier { - get { return TinkApp.Device.GetIdentifier(); } + get { return TinkApp.SmartDevice.Identifier; } } /// @@ -259,9 +271,11 @@ namespace TINK.ViewModel TinkApp.MinimumLogEventLevel = m_oMinimumLogEventLevel; // Update value to be serialized. TinkApp.UpdateLoggingLevel(m_oMinimumLogEventLevel); // Update logging server. + TinkApp.IsReportLevelVerbose = IsReportLevelVerbose; + TinkApp.LocksServices.SetActive(LocksServices.Services.Active); - TinkApp.GeolocationServices.SetActive(GeolocationServices.Active); + GeoloctionServicesContainer.SetActive(GeolocationServices.Active); TinkApp.LocksServices.SetTimeOut(TimeSpan.FromSeconds(LocksServices.ConnectTimeoutSec)); diff --git a/TINKLib/ViewModel/ViewModelHelper.cs b/TINKLib/ViewModel/ViewModelHelper.cs index 252fda6..ccad629 100644 --- a/TINKLib/ViewModel/ViewModelHelper.cs +++ b/TINKLib/ViewModel/ViewModelHelper.cs @@ -2,7 +2,7 @@ using Serilog; using System; using System.Threading.Tasks; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Model.Device; using TINK.Model.State; using TINK.Model.Station; @@ -10,8 +10,7 @@ using TINK.Model.User; using Xamarin.Forms; using TINK.Model.Bikes.Bike.BC; -using TINK.Model.Repository; -using TINK.Repository.Exception; +using TINK.Repository; using System.Net; using TINK.MultilingualResources; @@ -28,21 +27,21 @@ namespace TINK.ViewModel /// /// Gets station name from station object. /// - /// Station to get id from + /// Station to get id from /// - public static string GetStationName(this IStation p_oStation) + public static string GetStationName(this IStation station) { - if (p_oStation == null) + if (station == null) { return string.Empty; } - if (!string.IsNullOrEmpty(p_oStation.StationName)) + if (!string.IsNullOrEmpty(station.StationName)) { - return $"{p_oStation.StationName}, Nr. {p_oStation.Id}."; + return $"{station.StationName}, Nr. {station.Id}."; } - return GetStationName(p_oStation.Id); + return GetStationName(station.Id); } /// @@ -50,9 +49,9 @@ namespace TINK.ViewModel /// /// Station to get id from /// - public static string GetStationName(int p_iStationId) + public static string GetStationName(string station) { - return string.Format("{0} {1}", USER_FIENDLY_STATIONNUMBER_PREFIX, p_iStationId); + return string.Format("{0} {1}", USER_FIENDLY_STATIONNUMBER_PREFIX, station); } /// @@ -65,17 +64,29 @@ namespace TINK.ViewModel return int.Parse(p_strStationName.Replace(USER_FIENDLY_STATIONNUMBER_PREFIX, "").Trim()); } + /// Get full display name of a bike which includes id. + /// + /// If name is empty return Id as name. + /// + /// bike to get name for. + /// Display name of bike. + public static string GetFullDisplayName(this IBikeInfoMutable bike) + => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Description}, " : string.Empty)}Nr. {bike.Id}"; + /// Get the display name of a bike. /// bike to get name for. /// Display name of bike. public static string GetDisplayName(this IBikeInfoMutable bike) - { - var l_oId = bike.Id; - var l_oIsDemo = bike.IsDemo; + => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Description}" : $"{bike.Id}")}"; - // Not known how many whells cargo bike has. - return $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Description}, " : string.Empty)}Nr. {l_oId}"; - } + /// Get the display id of a bike. + /// + /// If name is empty id is used as name. For this reason return nothing in this case to avoid duplicate output of id. + /// + /// bike to get name for. + /// Display name of bike. + public static string GetDisplayId(this IBikeInfoMutable bike) + => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Id}" : string.Empty)}"; /// /// Maps state to color. @@ -108,6 +119,7 @@ namespace TINK.ViewModel } // An error occurred getting bikes information. +#if USCSHARP9 switch (exception) { case WebConnectFailureException: @@ -119,10 +131,30 @@ namespace TINK.ViewModel case DeserializationException: return AppResources.ActivityTextErrorDeserializationException; case WebException webException: - return string.Format(AppResources.ActivityTextErrorWebException, webException.Status); + return webException.Status == WebExceptionStatus.ProtocolError && webException.Response is HttpWebResponse webResponse + ? string.Format(AppResources.ActivityTextErrorWebExceptionProtocolError, webResponse.StatusDescription) + : string.Format(AppResources.ActivityTextErrorWebExceptionGeneralError, webException.Status.ToString()); default: return AppResources.ActivityTextErrorException; } +#else + if (exception is WebConnectFailureException) + return AppResources.ActivityTextErrorWebConnectFailureException; + if (exception is InvalidResponseException) + return AppResources.ActivityTextErrorInvalidResponseException; + if (exception is WebForbiddenException) + return AppResources.ActivityTextErrorWebForbiddenException; + if (exception is DeserializationException) + return AppResources.ActivityTextErrorDeserializationException; + if (exception is WebException webException) + { + return webException.Status == WebExceptionStatus.ProtocolError && webException.Response is HttpWebResponse webResponse + ? string.Format(AppResources.ActivityTextErrorWebExceptionProtocolError, webResponse.StatusDescription) + : string.Format(AppResources.ActivityTextErrorWebExceptionGeneralError, webException.Status.ToString()); + } + + return AppResources.ActivityTextErrorException; +#endif } diff --git a/TINKLib/ViewModel/WhatsNew/WhatsNewViewModel.cs b/TINKLib/ViewModel/WhatsNew/WhatsNewViewModel.cs index cc9e4d7..be5f7c8 100644 --- a/TINKLib/ViewModel/WhatsNew/WhatsNewViewModel.cs +++ b/TINKLib/ViewModel/WhatsNew/WhatsNewViewModel.cs @@ -6,7 +6,7 @@ using System.ComponentModel; namespace TINK.ViewModel.WhatsNew { - public class WhatsNewViewModel : INotifyPropertyChanged + public class WhatsNewViewModel { /// Constructs view model. /// @@ -95,12 +95,9 @@ namespace TINK.ViewModel.WhatsNew } /// Reference to view service object. - private IViewService ViewService; + private readonly IViewService ViewService; /// Reference to view service object. private Action ShowMasterDetail { get; } - - /// Fired whenever a property changes. - public event PropertyChangedEventHandler PropertyChanged; } } diff --git a/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItEventBased.cs b/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItEventBased.cs index 1711af5..002cc1b 100644 --- a/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItEventBased.cs +++ b/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItEventBased.cs @@ -158,7 +158,7 @@ namespace TestLockItBLE controlCharacteristic.ValueUpdated += Raise.EventWith(new object(), new CharacteristicUpdatedEventArgs(stateCharacteristic)); await lockState; }, - Throws.InstanceOf()); + Throws.InstanceOf()); } [Test] diff --git a/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItPolling.cs b/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItPolling.cs index 867e3cd..b8784ce 100644 --- a/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItPolling.cs +++ b/TestLockItBLE/Services/BluetoothLock/BLE/TestLockItPolling.cs @@ -137,7 +137,7 @@ namespace TestLockItBLE.Services.BluetoothLock.BLE // Use factory to create LockIt-object. var lockIt = LockItPolling.Authenticate(device, authTdo, adapter, cipher).Result; - Assert.That(async () => { var result = await lockIt.OpenAsync(); }, Throws.InstanceOf()); + Assert.That(async () => { var result = await lockIt.OpenAsync(); }, Throws.InstanceOf()); } [Test] diff --git a/TestLockItBLE/TestLockItBLE.csproj b/TestLockItBLE/TestLockItBLE.csproj index f66ed4a..0609a75 100644 --- a/TestLockItBLE/TestLockItBLE.csproj +++ b/TestLockItBLE/TestLockItBLE.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/TestLockItShared/Services/BluetoothLock/Exception/TestCouldntOpenInconsistentStateExecption.cs b/TestLockItShared/Services/BluetoothLock/Exception/TestCouldntOpenInconsistentStateExecption.cs index abfc532..aaa8bef 100644 --- a/TestLockItShared/Services/BluetoothLock/Exception/TestCouldntOpenInconsistentStateExecption.cs +++ b/TestLockItShared/Services/BluetoothLock/Exception/TestCouldntOpenInconsistentStateExecption.cs @@ -13,7 +13,7 @@ namespace TINK.Services.BluetoothLock.Exception Assert.That( ex.Message, - Is.EqualTo("Lock reports unknown bold position.")); + Is.EqualTo("Unexpected locking state \"Unknown\" detected after sending open command.")); } [Test] diff --git a/TestLockItShared/TestLockItShared.csproj b/TestLockItShared/TestLockItShared.csproj index 6718760..8dda01e 100644 --- a/TestLockItShared/TestLockItShared.csproj +++ b/TestLockItShared/TestLockItShared.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/TestShareeLib/Model/Bike/BC/TestBikeMutable.cs b/TestShareeLib/Model/Bike/BC/TestBikeMutable.cs index 3c2d452..1601cfa 100644 --- a/TestShareeLib/Model/Bike/BC/TestBikeMutable.cs +++ b/TestShareeLib/Model/Bike/BC/TestBikeMutable.cs @@ -13,13 +13,13 @@ namespace TestTINKLib private class BikeInfoMutable : TINK.Model.Bike.BC.BikeInfoMutable { public BikeInfoMutable( - int id, + string id, bool isDemo = false, IEnumerable group = null, WheelType? wheelType = null, TypeOfBike? typeOfBike = null, string description = null, - int? currentStationId = null, + string currentStationId = null, Uri operatorUri = null, TariffDescription tariffDescription = null, Func dateTimeProvider = null, @@ -32,13 +32,13 @@ namespace TestTINKLib public void TestConstruct() { var l_oBike = new BikeInfoMutable( - 42, + "42", false, new List { "TINK" }, WheelType.Two, TypeOfBike.Cargo); - Assert.AreEqual(42, l_oBike.Id); + Assert.AreEqual("42", l_oBike.Id); Assert.IsFalse(l_oBike.IsDemo); Assert.AreEqual(WheelType.Two, l_oBike.WheelType); Assert.AreEqual(TypeOfBike.Cargo, l_oBike.TypeOfBike); @@ -46,32 +46,32 @@ namespace TestTINKLib Assert.IsNull(l_oBike.CurrentStation); l_oBike = new BikeInfoMutable( - 17, + "17", true, new List { "TINK" }, WheelType.Mono, TypeOfBike.Allround, "Test description", - 1); + "1"); - Assert.AreEqual(17, l_oBike.Id); + Assert.AreEqual("17", l_oBike.Id); Assert.IsTrue(l_oBike.IsDemo); Assert.AreEqual(WheelType.Mono, l_oBike.WheelType); Assert.AreEqual(TypeOfBike.Allround, l_oBike.TypeOfBike); Assert.AreEqual(InUseStateEnum.Disposable, l_oBike.State.Value); - Assert.AreEqual(1, l_oBike.CurrentStation); + Assert.AreEqual("1", l_oBike.CurrentStation); } [Test] public void TestToString() { - var l_oBike = new BikeInfoMutable(3, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Cargo, dateTimeProvider: () => new DateTime(1970, 1, 1)); + var l_oBike = new BikeInfoMutable("3", false, new List { "TINK" }, WheelType.Two, TypeOfBike.Cargo, dateTimeProvider: () => new DateTime(1970, 1, 1)); Assert.AreEqual( "Id=3, wheel(s)=Two, type=Cargo, demo=False, state=Disposable, location=On the road.", l_oBike.ToString()); - l_oBike = new BikeInfoMutable(3, true, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", 5, dateTimeProvider: () => new DateTime(1970, 1, 1)); + l_oBike = new BikeInfoMutable("3", true, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", "5", dateTimeProvider: () => new DateTime(1970, 1, 1)); Assert.AreEqual( "Id=3, wheel(s)=Trike, type=Allround, demo=True, state=Disposable, location=Station 5.", l_oBike.ToString()); diff --git a/TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs b/TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs index f270ef4..45f78ea 100644 --- a/TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs +++ b/TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs @@ -16,32 +16,32 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock "Verify that no unspecific reference not set to... exception is thrown"); Assert.Throws( - () => new BikeInfo(new TINK.Model.Bike.BC.BikeInfo(12,1), null), + () => new BikeInfo(new TINK.Model.Bike.BC.BikeInfo("12", "1"), null), "Verify that no unspecific reference not set to... exception is thrown"); } [Test] public void TestCtorAvailable() { - Assert.AreEqual (12,new BikeInfo(12, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), 13).Id); - Assert.AreEqual(13, new BikeInfo(12, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), 13).CurrentStation); - Assert.AreEqual(InUseStateEnum.Disposable, new BikeInfo(12, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), 13).State.Value); + Assert.AreEqual ("12",new BikeInfo("12", 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").Id); + Assert.AreEqual("13", new BikeInfo("12", 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").CurrentStation); + Assert.AreEqual(InUseStateEnum.Disposable, new BikeInfo("12", 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "13").State.Value); } [Test] public void TestCtorRequested() { - Assert.AreEqual(12, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).Id); - Assert.AreEqual(112, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020,1,1), "a@b", 13, null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).LockInfo.Id); - Assert.AreEqual(InUseStateEnum.Reserved, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).State.Value); + Assert.AreEqual("12", new BikeInfo("12", 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).Id); + Assert.AreEqual(112, new BikeInfo("12", 112, new Guid(), null, null, null, new DateTime(2020,1,1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).LockInfo.Id); + Assert.AreEqual(InUseStateEnum.Reserved, new BikeInfo("12", 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).State.Value); } [Test] public void TestCtorBooked() { - Assert.AreEqual(12, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null /*operator uri*/).Id); - Assert.AreEqual(112, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null /*operator uri*/).LockInfo.Id); - Assert.AreEqual(InUseStateEnum.Booked, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null /*operator uri*/).State.Value); + Assert.AreEqual("12", new BikeInfo("12", 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).Id); + Assert.AreEqual(112, new BikeInfo("12", 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).LockInfo.Id); + Assert.AreEqual(InUseStateEnum.Booked, new BikeInfo("12", 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", "13", null /*operator uri*/).State.Value); } } } diff --git a/TestShareeLib/Model/Bike/TestBike.cs b/TestShareeLib/Model/Bike/TestBike.cs index 773a04b..2c47a73 100644 --- a/TestShareeLib/Model/Bike/TestBike.cs +++ b/TestShareeLib/Model/Bike/TestBike.cs @@ -10,14 +10,14 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike [Test] public void TestConstruct() { - var l_oBike = new Bike(43); - Assert.AreEqual(43, l_oBike.Id); + var l_oBike = new Bike("43"); + Assert.AreEqual("43", l_oBike.Id); Assert.AreEqual(null, l_oBike.TypeOfBike); Assert.AreEqual(null, l_oBike.WheelType); - l_oBike = new Bike(43, WheelType.Mono, TypeOfBike.Cargo); + l_oBike = new Bike("43", WheelType.Mono, TypeOfBike.Cargo); - Assert.AreEqual(43, l_oBike.Id); + Assert.AreEqual("43", l_oBike.Id); Assert.AreEqual(TypeOfBike.Cargo, l_oBike.TypeOfBike); Assert.AreEqual(WheelType.Mono, l_oBike.WheelType); } @@ -25,21 +25,21 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike [Test] public void TestCompare() { - var l_oBike1 = new Bike(43); - Assert.AreEqual(43, l_oBike1.Id); + var l_oBike1 = new Bike("43"); + Assert.AreEqual("43", l_oBike1.Id); Assert.AreEqual(null, l_oBike1.TypeOfBike); Assert.AreEqual(null, l_oBike1.WheelType); - var l_oBike2 = new Bike(42, WheelType.Two, TypeOfBike.Allround); + var l_oBike2 = new Bike("42", WheelType.Two, TypeOfBike.Allround); Assert.IsFalse(l_oBike1 == l_oBike2); - l_oBike2 = new Bike(43, WheelType.Mono, TypeOfBike.Allround); + l_oBike2 = new Bike("43", WheelType.Mono, TypeOfBike.Allround); Assert.IsFalse(l_oBike1 == l_oBike2); - l_oBike2 = new Bike(43, WheelType.Two, TypeOfBike.Cargo); + l_oBike2 = new Bike("43", WheelType.Two, TypeOfBike.Cargo); Assert.IsFalse(l_oBike1 == l_oBike2); - l_oBike2 = new Bike(43, null, null); + l_oBike2 = new Bike("43", null, null); Assert.IsTrue(l_oBike1 == l_oBike2); } } diff --git a/TestShareeLib/Model/Bike/TestBikeCollection.cs b/TestShareeLib/Model/Bike/TestBikeCollection.cs index 8fb709f..62c0e24 100644 --- a/TestShareeLib/Model/Bike/TestBikeCollection.cs +++ b/TestShareeLib/Model/Bike/TestBikeCollection.cs @@ -16,7 +16,7 @@ namespace TestTINKLib var l_oColl = new BikeCollection(); Assert.AreEqual(0, l_oColl.Count); - Assert.IsNull(l_oColl.GetById(1)); + Assert.IsNull(l_oColl.GetById("1")); } } } diff --git a/TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs b/TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs index eb5a663..1dcdfbd 100644 --- a/TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs +++ b/TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs @@ -17,13 +17,13 @@ namespace TestTINKLib private class BikeInfoMutable : TINK.Model.Bike.BC.BikeInfoMutable { public BikeInfoMutable( - int id, + string id, bool isDemo = false, IEnumerable group = null, WheelType? wheelType = null, TypeOfBike? typeOfBike = null, string description = null, - int? currentStationId = null, + string currentStationId = null, Uri operatorUri = null, TariffDescription tariffDescription = null, Func dateTimeProvider = null, @@ -38,72 +38,72 @@ namespace TestTINKLib { var l_oColl = new BikeCollectionMutable(); - l_oColl.Add(new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround)); + l_oColl.Add(new BikeInfoMutable("57", false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround)); - Assert.Throws(() => l_oColl.Add(new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo))); + Assert.Throws(() => l_oColl.Add(new BikeInfoMutable("57", false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo))); } [Test] public void TestUpdate_Null() { - var l_oBikeRequested = new BikeInfoMutable(20, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround); + var l_oBikeRequested = new BikeInfoMutable("20", false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround); l_oBikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, "john@long", "1234")); var l_oBikeColl = new BikeCollectionMutable { - new BikeInfoMutable(63, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround), - new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo), + new BikeInfoMutable("63", false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround), + new BikeInfoMutable("57", false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo), l_oBikeRequested, }; // Verify initial state - Assert.NotNull(l_oBikeColl.GetById(63)); - Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeColl.GetById(57).State.Value); - Assert.AreEqual(InUseStateEnum.Reserved, l_oBikeColl.GetById(20).State.Value); - Assert.Null(l_oBikeColl.GetById(33)); + Assert.NotNull(l_oBikeColl.GetById("63")); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeColl.GetById("57").State.Value); + Assert.AreEqual(InUseStateEnum.Reserved, l_oBikeColl.GetById("20").State.Value); + Assert.Null(l_oBikeColl.GetById("33")); l_oBikeColl.Update(null); // Verify modified state - Assert.Null(l_oBikeColl.GetById(63)); - Assert.Null(l_oBikeColl.GetById(57)); - Assert.Null(l_oBikeColl.GetById(20)); - Assert.Null(l_oBikeColl.GetById(33)); + Assert.Null(l_oBikeColl.GetById("63")); + Assert.Null(l_oBikeColl.GetById("57")); + Assert.Null(l_oBikeColl.GetById("20")); + Assert.Null(l_oBikeColl.GetById("33")); } [Test] public void TestUpdate() { - var l_oBikeRequested = new BikeInfoMutable(20, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround); + var l_oBikeRequested = new BikeInfoMutable("20", false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround); l_oBikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, "john@long", "1234")); var l_oBikeColl = new BikeCollectionMutable { - new BikeInfoMutable(63, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround), - new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo), + new BikeInfoMutable("63", false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround), + new BikeInfoMutable("57", false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo), l_oBikeRequested, }; // Verify initial state - Assert.NotNull(l_oBikeColl.GetById(63)); // Will be removed - Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeColl.GetById(57).State.Value); // Will be requested - Assert.AreEqual(InUseStateEnum.Reserved, l_oBikeColl.GetById(20).State.Value); // Will be booked - Assert.Null(l_oBikeColl.GetById(33)); // + Assert.NotNull(l_oBikeColl.GetById("63")); // Will be removed + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeColl.GetById("57").State.Value); // Will be requested + Assert.AreEqual(InUseStateEnum.Reserved, l_oBikeColl.GetById("20").State.Value); // Will be booked + Assert.Null(l_oBikeColl.GetById("33")); // var l_oBikeResponse = new List { - new BikeInfo(57, false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", 7, null /*operator uri*/, null, DateTime.Now, "john@long,", "1234"), - new BikeInfo(20, false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", 7, null /*operator uri*/, null, DateTime.Now, "john@long,", "1234"), - new BikeInfo(33, 7, null /*operator uri*/, null, false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround), + new BikeInfo("57", false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", "7", null /*operator uri*/, null, DateTime.Now, "john@long,", "1234"), + new BikeInfo("20", false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", "7", null /*operator uri*/, null, DateTime.Now, "john@long,", "1234"), + new BikeInfo("33", "7", null /*operator uri*/, null, false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround), }; l_oBikeColl.Update(l_oBikeResponse); // Verify modified state - Assert.Null(l_oBikeColl.GetById(63)); - Assert.AreEqual(InUseStateEnum.Booked, l_oBikeColl.GetById(57).State.Value); - Assert.AreEqual(InUseStateEnum.Booked, l_oBikeColl.GetById(20).State.Value); - Assert.NotNull(l_oBikeColl.GetById(33)); + Assert.Null(l_oBikeColl.GetById("63")); + Assert.AreEqual(InUseStateEnum.Booked, l_oBikeColl.GetById("57").State.Value); + Assert.AreEqual(InUseStateEnum.Booked, l_oBikeColl.GetById("20").State.Value); + Assert.NotNull(l_oBikeColl.GetById("33")); } } } diff --git a/TestShareeLib/Model/Connector/TestCachetimings.cs b/TestShareeLib/Model/Connector/TestCachetimings.cs new file mode 100644 index 0000000..80509f6 --- /dev/null +++ b/TestShareeLib/Model/Connector/TestCachetimings.cs @@ -0,0 +1,160 @@ +using MonkeyCache.FileStore; +using NUnit.Framework; +using Serilog; +using System; +using TINK.Model.Connector; +using TINK.Model.Services.CopriApi.ServerUris; +using UITest.Fixtures.Connector; + +namespace TestTINKLib.Fixtures.Misc +{ + [TestFixture] + [Explicit("Run with care because this stress impact live system.")] + [Category(TestCopriCallsHttps.CATEGORY_REQUIRESCOPRI)] + public class TestCachetimings + { + [Test] +#if !NOLIVESERVER + [Explicit("Run with care because this stress impact live system.")] + //[Category(TestCopriCallsHttps.CATEGORY_USESLIVESERVER)] +#elif !NODEVELSERVER + [Category(TestCopriCallsHttps.CATEGORY_USESDEVELSERVER)] +#endif + public void TestCachedQuery_GetStationsAll( +#if NOLIVESERVER + [Values(CopriServerUriList.SHAREE_DEVEL)] string url, +#elif NODEVELSERVER + [Values(CopriServerUriList.SHAREE_LIVE)] string url, +#else + [Values(CopriServerUriList.SHAREE_DEVEL, CopriServerUriList.SHAREE_LIVE)] string url, +#endif + [Values(";", "6103_4da3044c8657a04ba60e2eaa753bc51a_;javaminister@gmail.com")] string cookieAndMail) + { + Barrel.ApplicationId = GetType().Name + nameof(TestCachedQuery_GetStationsAll); + + Log .Logger = new LoggerConfiguration() + .MinimumLevel.ControlledBy(new Serilog.Core.LoggingLevelSwitch(Serilog.Events.LogEventLevel.Error)) + .WriteTo.Debug() + .CreateLogger(); + + var connector = ConnectorFactory.Create( + true, // Is connected + new Uri(url), + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/" + nameof(TestCachedQuery_GetStationsAll), + cookieAndMail.Split(';')[0], + cookieAndMail.Split(';')[1], + TimeSpan.FromSeconds(3)); // See task #97 for need of custom expiresAfter value. + + var start = DateTime.Now; + + var queryIndex = 1; + while (DateTime.Now.Subtract(start).TotalSeconds < 60) + { + Log.ForContext().Information($"Query #{queryIndex}"); + + var result = connector.Query.GetBikesAndStationsAsync().Result; + Assert.IsNull(result.Exception, $"Exception message: {result.Exception?.Message}"); + + queryIndex++; + } + } + + /// + /// From COPRI version v4.1 switched from TINK devel https://tinkwwp.copri-bike.de/APIjsonserver to sharee devel https://shareeapp-primary.copri-bike.de/APIjsonserver + /// + /// + /// + [Test] +#if !NOLIVESERVER + [Explicit("Run with care because this stress impact live system.")] + //[Category(TestCopriCallsHttps.CATEGORY_USESLIVESERVER)] +#elif !NODEVELSERVER + [Category(TestCopriCallsHttps.CATEGORY_USESDEVELSERVER)] +#endif + public void TestCachedQuery_GetBikes( +#if NOLIVESERVER + [Values(CopriServerUriList.SHAREE_DEVEL)] string url, +#elif NODEVELSERVER + [Values(CopriServerUriList.SHAREE_LIVE)] string url, +#else + [Values(CopriServerUriList.SHAREE_DEVEL, CopriServerUriList.SHAREE_LIVE)] string url, +#endif + [Values(";", "6103_4da3044c8657a04ba60e2eaa753bc51a_;javaminister@gmail.com")] string cookieAndMail) + { + Barrel.ApplicationId = GetType().Name + nameof(TestCachedQuery_GetBikes); + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.ControlledBy(new Serilog.Core.LoggingLevelSwitch(Serilog.Events.LogEventLevel.Error)) + .WriteTo.Debug() + .CreateLogger(); + + var connector = ConnectorFactory.Create( + true, // Is connected + new Uri(url), + "Testsharee.bikeApp/3.0.223 AutomatedTestEnvirnoment/" + nameof(TestCachedQuery_GetBikes), + cookieAndMail.Split(';')[0], + cookieAndMail.Split(';')[1], + TimeSpan.FromSeconds(3)); // See task #97 for need of custom expiresAfter value. + + var start = DateTime.Now; + + var queryIndex = 1; + while (DateTime.Now.Subtract(start).TotalSeconds < 60) + { + Log.ForContext().Information($"Query #{queryIndex}"); + + var result = connector.Query.GetBikesAsync().Result; + Assert.IsNull(result.Exception, $"Exception message: {result.Exception?.Message}"); + + queryIndex++; + } + } + + [Test] +#if !NOLIVESERVER + [Explicit("Run with care because this stress impact live system.")] + //[Category(TestCopriCallsHttps.CATEGORY_USESLIVESERVER)] +#elif !NODEVELSERVER + [Category(TestCopriCallsHttps.CATEGORY_USESDEVELSERVER)] +#endif + public void TestCachedQuery_GetBikesOccupied( +#if NOLIVESERVER + [Values(CopriServerUriList.SHAREE_DEVEL)] string url, +#elif NODEVELSERVER + [Values(CopriServerUriList.SHAREE_LIVE)] string url, +#else + [Values(CopriServerUriList.SHAREE_DEVEL, CopriServerUriList.SHAREE_LIVE)] string url, +#endif + [Values("6103_4da3044c8657a04ba60e2eaa753bc51a_;javaminister@gmail.com")] string cookieAndMail) + { + Barrel.ApplicationId = GetType().Name + nameof(TestCachedQuery_GetBikes); + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.ControlledBy(new Serilog.Core.LoggingLevelSwitch(Serilog.Events.LogEventLevel.Verbose)) + .WriteTo.Debug() + .WriteTo.File($"Log{nameof(TestCachedQuery_GetBikes)}.log") + .CreateLogger(); + + var connector = ConnectorFactory.Create( + true, // Is connected + new Uri(url), + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/" + nameof(TestCachedQuery_GetBikes), + cookieAndMail.Split(';')[0], + cookieAndMail.Split(';')[1], + TimeSpan.FromSeconds(3)); // See task #97 for need of custom expiresAfter value. + + var start = DateTime.Now; + + var queryIndex = 1; + while (DateTime.Now.Subtract(start).TotalSeconds < 60) + { + Log.ForContext().Information($"Query #{queryIndex}"); + + var result = connector.Query.GetBikesOccupiedAsync().Result; + Assert.IsNull(result.Exception, $"Exception message: {result.Exception?.Message}"); + + queryIndex++; + } + } + } +} diff --git a/TestShareeLib/Model/Connector/TestConnectorFactory.cs b/TestShareeLib/Model/Connector/TestConnectorFactory.cs new file mode 100644 index 0000000..c6afca2 --- /dev/null +++ b/TestShareeLib/Model/Connector/TestConnectorFactory.cs @@ -0,0 +1,30 @@ +using MonkeyCache.FileStore; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TINK.Model.Connector; + +namespace TestTINKLib.Fixtures.ObjectTests.Connector +{ + [TestFixture] + public class TestConnectorFactory + { + [Test] + public void TestCreate() + { + if (string.IsNullOrEmpty(Barrel.ApplicationId)) + { + Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + } + + var connector = ConnectorFactory.Create(false, new Uri("https://1.2.3.4"), "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", "123456789", "a@b"); + Assert.AreEqual(typeof(ConnectorCache), connector.GetType()); + + connector = ConnectorFactory.Create(true, new Uri("https://1.2.3.4"), "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", "123456789", "a@b"); + Assert.AreEqual(typeof(TINK.Model.Connector.Connector), connector.GetType()); + } + } +} diff --git a/TestShareeLib/Model/Connector/TestCopriProviderHttps.cs b/TestShareeLib/Model/Connector/TestCopriProviderHttps.cs new file mode 100644 index 0000000..70cd820 --- /dev/null +++ b/TestShareeLib/Model/Connector/TestCopriProviderHttps.cs @@ -0,0 +1,514 @@ +using MonkeyCache.FileStore; +using Newtonsoft.Json; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Threading.Tasks; +using TINK.Model.Services.CopriApi; +using TINK.Repository; +using TINK.Repository.Exception; +using TINK.Repository.Response; + + +namespace TestTINKLib.Fixtures.ObjectTests.Connector +{ + [TestFixture] + public class TestCopriProviderHttps + { + private const string BIKESAVAILABLE = @"{ + ""copri_version"" : ""4.1.0.0"", + ""bikes"" : {}, + ""response_state"" : ""OK"", + ""apiserver"" : ""https://app.tink-konstanz.de"", + ""authcookie"" : """", + ""response"" : ""bikes_available"", + ""bikes"" : { + ""2352"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""1"", + ""gps"" : { ""latitude"": ""47.669888"", ""longitude"": ""9.167749"" }, + ""station"" : ""9"" + } + } + }"; + + private const string BIKESOCCUPIED = @"{ + ""authcookie"" : ""6103_f782a208d9399291ba8d086b5dcc2509_12345678"", + ""debuglevel"" : ""2"", + ""user_group"" : [ ""TINK"" ], + ""user_id"" : ""javaminister@gmail.com"", + ""response"" : ""user_bikes_occupied"", + ""response_state"" : ""OK"", + ""response_text"" : ""Die Liste der reservierten und gebuchten Fahrräder wurde erfolgreich geladen"", + ""apiserver"" : ""https://tinkwwp.copri-bike.de"", + ""bikes_occupied"" : { + ""89004"" : { + ""start_time"" : ""2018-01-27 17:33:00.989464+01"", + ""station"" : ""9"", + ""unit_price"" : ""2.00"", + ""tariff_description"": { + ""free_hours"" : ""0.5"", + ""name"" : ""TINK Tarif"", + ""max_eur_per_day"" : ""9.00"" + }, + ""timeCode"" : ""2061"", + ""description"" : ""Cargo Long"", + ""bike"" : ""4"", + ""total_price"" : ""20.00"", + ""state"" : ""requested"", + ""real_hours"" : ""66.05"", + ""bike_group"" : [ ""TINK"" ], + ""now_time"" : ""2018-01-30 11:36:45"", + ""request_time"" : ""2018-01-27 17:33:00.989464+01"", + ""computed_hours"" : ""10.0"" + } + } + }"; + + private const string STATIONSALL = @"{ + ""copri_version"" : ""4.1.0.0"", + ""stations"" : { + ""5"" : { + ""station"" : ""5"", + ""bike_soll"" : ""0"", + ""bike_ist"" : ""7"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.66756"", ""longitude"": ""9.16477"" }, + ""state"" : ""available"", + ""description"" : """" + }, + ""13"" : { + ""station"" : ""13"", + ""bike_soll"" : ""4"", + ""bike_ist"" : ""1"", + ""station_group"" : [ ""TINK"" ], + ""gps"" : { ""latitude"": ""47.657756"", ""longitude"": ""9.176084"" }, + ""state"" : ""available"", + ""description"" : """" + }, + ""30"" : { + ""station"" : ""30"", + ""bike_soll"" : ""5"", + ""bike_ist"" : ""0"", + ""station_group"" : [ ""TINK"", ""Konrad"" ], + ""gps"" : { ""latitude"": ""47.657766"", ""longitude"": ""9.176094"" }, + ""state"" : ""available"", + ""description"" : ""Test für Stadtradstation"" + } + }, + ""user_group"" : [ ""Konrad"", ""TINK"" ], + ""response_state"" : ""OK"", + ""authcookie"" : ""6103_f782a208d9399291ba8d086b5dcc2509_12345678"", + ""debuglevel"" : ""2"", + ""response"" : ""stations_all"", + ""user_id"" : ""javaminister@gmail.com"", + ""apiserver"" : ""https://tinkwwp.copri-bike.de"" + }"; + + [Test] + public async Task TestGetBikesAvailable_NotExpired() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesAvailableExpired.Returns(false); + cache.GetBikesAvailableAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESAVAILABLE))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", // Merchant id + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", // User agent + "456", // cookie + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesAvailable(); + + Assert.AreEqual(1, bikes.Response.bikes.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, bikes.Source.Name); + Assert.IsNull(bikes.Exception); + } + + + [Test] + public async Task TestGetBikesAvailable_ExpiredForceCache() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesAvailableExpired.Returns(true); + cache.GetBikesAvailableAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESAVAILABLE))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesAvailable(true); + + Assert.AreEqual(1, bikes.Response.bikes.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, bikes.Source.Name); + Assert.IsNull(bikes.Exception); + } + + [Test] + public async Task TestGetBikesAvailable_Expired() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesAvailableExpired.Returns(true); + https.GetBikesAvailableAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESAVAILABLE))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesAvailable(); + + Assert.AreEqual(1, bikes.Response.bikes.Count); + Assert.AreEqual(typeof(CopriCallsHttps).Name, bikes.Source.Name); + Assert.IsNull(bikes.Exception); + } + + [Test] + public async Task TestGetBikesAvailable_Exception() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesAvailableExpired.Returns(true); + cache.GetBikesAvailableAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESAVAILABLE))); + https.GetBikesAvailableAsync().Returns(x => { throw new WebConnectFailureException("Bang...", new Exception()); }); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesAvailable(); + + Assert.AreEqual(1, bikes.Response.bikes.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, bikes.Source.Name); + Assert.AreEqual("Bang...", bikes.Exception.Message); + } + + [Test] + public async Task TestGetBikesOccupied_NotExpired() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesOccupiedExpired.Returns(false); + cache.GetBikesOccupiedAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESOCCUPIED))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "12345678", // Merchant id + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", // User agent + "f782a208d9399291ba8d086b5dcc2509", // Auth cookie + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesOccupied(); + + Assert.AreEqual(1, bikes.Response.bikes_occupied.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, bikes.Source.Name); + Assert.IsNull(bikes.Exception); + } + + [Test] + public async Task TestGetBikesOccupied_ExpiredForceCache() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesOccupiedExpired.Returns(true); + cache.GetBikesOccupiedAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESOCCUPIED))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "12345678", // Merchant id + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", // User agent + "f782a208d9399291ba8d086b5dcc2509", // Auth cookie + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesOccupied(true); + + Assert.AreEqual(1, bikes.Response.bikes_occupied.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, bikes.Source.Name); + Assert.IsNull(bikes.Exception); + } + + [Test] + public async Task TestGetBikesOccupied_Expired() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesOccupiedExpired.Returns(true); + https.GetBikesOccupiedAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESOCCUPIED))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "12345678", // Merchant id + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", // User agent + "f782a208d9399291ba8d086b5dcc2509", // Auth cookie + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesOccupied(); + + Assert.AreEqual(1, bikes.Response.bikes_occupied.Count); + Assert.AreEqual(typeof(CopriCallsHttps).Name, bikes.Source.Name); + Assert.IsNull(bikes.Exception); + } + + [Test] + public async Task TestGetBikesOccupied_Exception() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsBikesOccupiedExpired.Returns(true); + cache.GetBikesOccupiedAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(BIKESOCCUPIED))); + https.GetBikesOccupiedAsync().Returns(x => { throw new WebConnectFailureException("Bang...", new Exception()); }); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "12345678", // Merchant id + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", // User agent + "f782a208d9399291ba8d086b5dcc2509", // Auth cookie + cacheServer: cache, + httpsServer: https); + + var bikes = await provider.GetBikesOccupied(); + + Assert.AreEqual(1, bikes.Response.bikes_occupied.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, bikes.Source.Name); + Assert.AreEqual("Bang...", bikes.Exception.Message); + } + + [Test] + public async Task TestGetStations_NotExpired() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsStationsExpired.Returns(false); + cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(STATIONSALL))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", // Merchant id + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", // User agent + "456", // cookie + cacheServer: cache, + httpsServer: https); + + var stations = await provider.GetStations(); + + Assert.AreEqual(3, stations.Response.stations.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, stations.Source.Name); + Assert.IsNull(stations.Exception); + } + + [Test] + public async Task TestGetStations_ExpiredForceCache() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsStationsExpired.Returns(true); + cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(STATIONSALL))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", + cacheServer: cache, + httpsServer: https); + + var stations = await provider.GetStations(true); + + Assert.AreEqual(3, stations.Response.stations.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, stations.Source.Name); + Assert.IsNull(stations.Exception); + } + + [Test] + public async Task TestGetStations_Expired() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsStationsExpired.Returns(true); + https.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(STATIONSALL))); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", + cacheServer: cache, + httpsServer: https); + + var stations = await provider.GetStations(); + + Assert.AreEqual(3, stations.Response.stations.Count); + Assert.AreEqual(typeof(CopriCallsHttps).Name, stations.Source.Name); + Assert.IsNull(stations.Exception); + } + + [Test] + public async Task TestGetStations_Exception() + { + var cache = Substitute.For(); + var https = Substitute.For(); + + cache.IsStationsExpired.Returns(true); + cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject(STATIONSALL))); + https.GetStationsAsync().Returns(x => { throw new WebConnectFailureException("Bang...", new Exception()); }); + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", + cacheServer: cache, + httpsServer: https); + + var stations = await provider.GetStations(); + + Assert.AreEqual(3, stations.Response.stations.Count); + Assert.AreEqual(typeof(CopriCallsMonkeyStore).Name, stations.Source.Name); + Assert.AreEqual("Bang...", stations.Exception.Message); + } + + [Test] + public async Task Test_AddToCache_Stations() + { + if (string.IsNullOrEmpty(Barrel.ApplicationId)) + { + Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + } + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123456789", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default"); + + var stations = await provider.GetStations(true); + Assert.AreEqual(0, stations.Response.stations.Count); + + try + { + // Do not add if an excption occurred + provider.AddToCache(new Result(typeof(CopriCallsHttps), JsonConvert.DeserializeObject(STATIONSALL), new System.Exception("Bang..."))); + stations = await provider.GetStations(true); + Assert.AreEqual(0, stations.Response.stations.Count); + + // Do not add if results from cache + provider.AddToCache(new Result(typeof(CopriCallsMonkeyStore), JsonConvert.DeserializeObject(STATIONSALL))); + stations = await provider.GetStations(true); + Assert.AreEqual(0, stations.Response.stations.Count); + + // Add result + provider.AddToCache(new Result(typeof(CopriCallsHttps), JsonConvert.DeserializeObject(STATIONSALL))); + stations = await provider.GetStations(true); + Assert.AreEqual(3, stations.Response.stations.Count); + } + finally + { + Barrel.Current.EmptyAll(); + } + } + + [Test] + public async Task Test_AddToCache_BikesAvailable() + { + if (string.IsNullOrEmpty(Barrel.ApplicationId)) + { + Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + } + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123456789", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default"); + + var bikes = await provider.GetBikesAvailable(true); + Assert.AreEqual(0, bikes.Response.bikes.Count); + + try + { + // Do not add if an excption occurred + provider.AddToCache(new Result(typeof(CopriCallsHttps), JsonConvert.DeserializeObject(BIKESAVAILABLE), new System.Exception("Bang..."))); + bikes = await provider.GetBikesAvailable(true); + Assert.AreEqual(0, bikes.Response.bikes.Count); + + // Do not add if results from cache + provider.AddToCache(new Result(typeof(CopriCallsMonkeyStore), JsonConvert.DeserializeObject(BIKESAVAILABLE))); + bikes = await provider.GetBikesAvailable(true); + Assert.AreEqual(0, bikes.Response.bikes.Count); + + // Add result + provider.AddToCache(new Result(typeof(CopriCallsHttps), JsonConvert.DeserializeObject(BIKESAVAILABLE))); + bikes = await provider.GetBikesAvailable(true); + Assert.AreEqual(1, bikes.Response.bikes.Count); + } + finally + { + Barrel.Current.EmptyAll(); + } + } + + + [Test] + public async Task Test_AddToCache_BikesOccupied() + { + if (string.IsNullOrEmpty(Barrel.ApplicationId)) + { + Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + } + + var provider = new CopriProviderHttps( + new Uri("http://1.2.3.4"), + "123456789", + "TestTINKApp/3.0.127 AutomatedTestEnvirnoment/Default", // User agent + "876"); + + var bikes = await provider.GetBikesOccupied(true); + Assert.AreEqual(0, bikes.Response.bikes_occupied.Count); + + try + { + // Do not add if an excption occurred + provider.AddToCache(new Result(typeof(CopriCallsHttps), JsonConvert.DeserializeObject(BIKESOCCUPIED), new System.Exception("Bang..."))); + bikes = await provider.GetBikesOccupied(true); + Assert.AreEqual(0, bikes.Response.bikes_occupied.Count); + + // Do not add if results from cache + provider.AddToCache(new Result(typeof(CopriCallsMonkeyStore), JsonConvert.DeserializeObject(BIKESOCCUPIED))); + bikes = await provider.GetBikesOccupied(true); + Assert.AreEqual(0, bikes.Response.bikes_occupied.Count); + + // Add result + provider.AddToCache(new Result(typeof(CopriCallsHttps), JsonConvert.DeserializeObject(BIKESOCCUPIED))); + bikes = await provider.GetBikesOccupied(true); + Assert.AreEqual(1, bikes.Response.bikes_occupied.Count); + } + finally + { + Barrel.Current.EmptyAll(); + } + } + } +} diff --git a/TestShareeLib/Model/Connector/TestUpdaterJSON.cs b/TestShareeLib/Model/Connector/TestUpdaterJSON.cs new file mode 100644 index 0000000..f036e51 --- /dev/null +++ b/TestShareeLib/Model/Connector/TestUpdaterJSON.cs @@ -0,0 +1,866 @@ +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.Linq; +using TINK.Model; +using TINK.Model.Bike; +using TINK.Model.Connector; +using TINK.Repository.Response; +using TINK.Model.State; +using TINK.Repository; + +using static TINK.Repository.CopriCallsMemory; + +using BikeInfo = TINK.Model.Bike.BluetoothLock.BikeInfo; +using TINK.Model.User.Account; + +namespace TestTINKLib.Fixtures.Connector +{ + + [TestFixture] + public class TestUpdaterJSON + { + [Test] + public void TestGetAllStations() + { + var l_oStationsTarget = UpdaterJSON.GetStationsAllMutable(new CopriCallsMemory(SampleSets.Set2, 1).GetStationsAsync().Result); + + Assert.AreEqual(9, l_oStationsTarget.Count); + + // Check first entry. + Assert.NotNull(l_oStationsTarget.GetById("4")); + Assert.AreEqual("TINK", String.Join(",", l_oStationsTarget.GetById("4").Group.ToArray())); + Assert.AreEqual("4", l_oStationsTarget.GetById("4").Id); + Assert.AreEqual(47.6586936667, l_oStationsTarget.GetById("4").Position.Latitude); + Assert.AreEqual(9.16863116667, l_oStationsTarget.GetById("4").Position.Longitude); + + Assert.NotNull(l_oStationsTarget.GetById("14")); + Assert.AreEqual("Konrad", String.Join(",", l_oStationsTarget.GetById("14").Group.ToArray())); + + Assert.NotNull(l_oStationsTarget.GetById("31")); + Assert.AreEqual("TINK,Konrad", String.Join(",", l_oStationsTarget.GetById("31").Group.ToArray())); + Assert.AreEqual("Südstadt Station", l_oStationsTarget.GetById("31").StationName); + } + + [Test] + public void TestUpdateBikesAvailable_BikeNr5GetBooked() + { + // Bike 5 is availalbe. + var l_oBikesTarget = UpdaterJSON.GetBikesAvailable( + GetBikesAvailable(TinkApp.MerchantId, p_eSampleSet: SampleSets.Set2, p_lStageIndex: 1)); + + Assert.AreEqual(12, l_oBikesTarget.Count, "Bike 5 is available an must be part of available bikes collection"); + + // Verify state of bike 5. + Assert.NotNull(l_oBikesTarget.GetById("5")); + Assert.AreEqual("5", l_oBikesTarget.GetById("5").Id); + Assert.AreEqual(TypeOfBike.Cargo, l_oBikesTarget.GetById("5").TypeOfBike); + Assert.AreEqual(WheelType.Two, l_oBikesTarget.GetById("5").WheelType); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikesTarget.GetById("5").State.Value); + Assert.IsNull(l_oBikesTarget.GetById("5").State.From); // Sommer/ Winterzeit! + Assert.IsNull(l_oBikesTarget.GetById("5").State.Code); + + // Bike 5 is reserved. + // Count of bikes must decrease and bike #5 no more in list of bikes. + l_oBikesTarget = UpdaterJSON.GetBikesAvailable( + GetBikesAvailable(TinkApp.MerchantId, p_eSampleSet: SampleSets.Set2, p_lStageIndex: 2)); + + Assert.AreEqual(11, l_oBikesTarget.Count, "One bike (nr. 5) got reserved"); + Assert.Null(l_oBikesTarget.GetById("5"), "Bike 5 got requested and must not be part of available bikes collection"); + + // Bike 5 is booked. + // Count of bikes must decrease and bike #5 no more in list of bikes. + l_oBikesTarget = UpdaterJSON.GetBikesAvailable( + GetBikesAvailable(TinkApp.MerchantId, p_eSampleSet: SampleSets.Set2, p_lStageIndex: 3)); + + Assert.Null(l_oBikesTarget.GetById("5"), "Bike 5 got booked and must not be part of available bikes collection"); + Assert.IsNull(l_oBikesTarget.GetById("5")); + } + + [Test] + public void TestUpdateBikesOccupied_BikeNr5GetBooked() + { + var l_oBikesTarget = UpdaterJSON.GetBikesOccupied( + GetBikesOccupied("4da3044c8657a04ba60e2eaa753bc51a", SampleSets.Set2, 1), + "a@B", + () => new DateTime(2017, 11, 28, 14, 8, 14)); // Date time now for bikes which are reserved + + // Check initial count of bikes. + Assert.AreEqual(2, l_oBikesTarget.Count); + + // Bike 5 is reserved + l_oBikesTarget = UpdaterJSON.GetBikesOccupied( + GetBikesOccupied("4da3044c8657a04ba60e2eaa753bc51a", SampleSets.Set2, 2), + "a@B", + () => new DateTime(2017, 11, 28, 14, 08, 36).Add(new TimeSpan(0, 2, 0))); // Date time now for bikes which are reserved + + Assert.AreEqual(3, l_oBikesTarget.Count, "One bike (nr. 5) got reserved"); + + Assert.NotNull(l_oBikesTarget.GetById("5")); + Assert.AreEqual(InUseStateEnum.Reserved, l_oBikesTarget.GetById("5").State.Value); + Assert.AreEqual("5", l_oBikesTarget.GetById("5").Id); + Assert.AreEqual(TypeOfBike.Cargo, l_oBikesTarget.GetById("5").TypeOfBike); + Assert.AreEqual(WheelType.Two, l_oBikesTarget.GetById("5").WheelType); + Assert.AreEqual(DateTime.Parse("2017-11-28 14:07:13.745568+01"), l_oBikesTarget.GetById("5").State.From); // Sommer/ Winterzeit! + Assert.AreEqual("2360", l_oBikesTarget.GetById("5").State.Code); + + + // Bike 5 is booked + l_oBikesTarget = UpdaterJSON.GetBikesOccupied( + GetBikesOccupied("4da3044c8657a04ba60e2eaa753bc51a", SampleSets.Set2, 3), + "a@B", + () => DateTime.Now); + + Assert.AreEqual(3, l_oBikesTarget.Count, "One bike (nr. 5) got booked"); + + Assert.IsNotNull(l_oBikesTarget.GetById("5")); + Assert.AreEqual(InUseStateEnum.Booked, l_oBikesTarget.GetById("5").State.Value); + Assert.AreEqual("5", l_oBikesTarget.GetById("5").Id); + Assert.AreEqual(TypeOfBike.Cargo, l_oBikesTarget.GetById("5").TypeOfBike); + Assert.AreEqual(WheelType.Two, l_oBikesTarget.GetById("5").WheelType); + Assert.AreEqual(DateTime.Parse("2017 -11-28 14:08:32.756368+01"), l_oBikesTarget.GetById("5").State.From); // Sommer/ Winterzeit! + Assert.AreEqual("2360", l_oBikesTarget.GetById("5").State.Code); + } + + public void TestGetBikesAvailable_BikeNr5GetBooked() + { + // Bike 5 is availalbe. + var l_oBikesTarget = UpdaterJSON.GetBikesAvailable(GetBikesAvailable(TinkApp.MerchantId, p_eSampleSet: SampleSets.Set2, p_lStageIndex: 1)); + + Assert.AreEqual(11, l_oBikesTarget.Count, "Bike 5 is available an must be part of available bikes collection"); + + // Verify state of bike 5. + Assert.NotNull(l_oBikesTarget.GetById("5")); + Assert.AreEqual(5, l_oBikesTarget.GetById("5").Id); + Assert.AreEqual(TypeOfBike.Cargo, l_oBikesTarget.GetById("5").TypeOfBike); + Assert.AreEqual(WheelType.Two, l_oBikesTarget.GetById("5").WheelType); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikesTarget.GetById("5").State.Value); + Assert.IsNull(l_oBikesTarget.GetById("5").State.From); // Sommer/ Winterzeit! + Assert.IsNull(l_oBikesTarget.GetById("5").State.Code); + + // Bike 5 is reserved. + // Count of bikes must decrease and bike #5 no more in list of bikes. + l_oBikesTarget = UpdaterJSON.GetBikesAvailable(GetBikesAvailable(TinkApp.MerchantId, p_eSampleSet: SampleSets.Set2, p_lStageIndex: 2)); + + Assert.AreEqual(10, l_oBikesTarget.Count, "One bike (nr. 5) got reserved"); + Assert.Null(l_oBikesTarget.GetById("5"), "Bike 5 got requested and must not be part of available bikes collection"); + + // Bike 5 is booked. + // Count of bikes must decrease and bike #5 no more in list of bikes. + l_oBikesTarget = UpdaterJSON.GetBikesAvailable(GetBikesAvailable(TinkApp.MerchantId, p_eSampleSet: SampleSets.Set2, p_lStageIndex: 3)); + + Assert.Null(l_oBikesTarget.GetById("5"), "Bike 5 got booked and must not be part of available bikes collection"); + Assert.IsNull(l_oBikesTarget.GetById("5")); + } + + [Test] + public void TestGetBikesOccupied_BikeNr5GetBooked() + { + var l_oBikesTarget = UpdaterJSON.GetBikesOccupied( + GetBikesOccupied("4da3044c8657a04ba60e2eaa753bc51a", SampleSets.Set2, 1), + "a@B", + () => new DateTime(2017, 11, 28, 14, 8, 14)); // Date time now for bikes which are reserved + + // Check initial count of bikes. + Assert.AreEqual(2, l_oBikesTarget.Count); + + // Bike 5 is reserved + l_oBikesTarget = UpdaterJSON.GetBikesOccupied( + GetBikesOccupied("4da3044c8657a04ba60e2eaa753bc51a", SampleSets.Set2, 2), + "a@B", + () => new DateTime(2017, 11, 28, 14, 08, 36).Add(new TimeSpan(0, 2, 0))); // Date time now for bikes which are reserved + + Assert.AreEqual(3, l_oBikesTarget.Count, "One bike (nr. 5) got reserved"); + + Assert.NotNull(l_oBikesTarget.GetById("5")); + Assert.AreEqual(InUseStateEnum.Reserved, l_oBikesTarget.GetById("5").State.Value); + Assert.AreEqual("5", l_oBikesTarget.GetById("5").Id); + Assert.AreEqual(TypeOfBike.Cargo, l_oBikesTarget.GetById("5").TypeOfBike); + Assert.AreEqual(WheelType.Two, l_oBikesTarget.GetById("5").WheelType); + Assert.AreEqual(DateTime.Parse("2017-11-28 14:07:13.745568+01"), l_oBikesTarget.GetById("5").State.From); // Sommer/ Winterzeit! + Assert.AreEqual("2360", l_oBikesTarget.GetById("5").State.Code); + + + // Bike 5 is booked + l_oBikesTarget = UpdaterJSON.GetBikesOccupied( + CopriCallsMemory.GetBikesOccupied("4da3044c8657a04ba60e2eaa753bc51a", SampleSets.Set2, 3), + "a@B", + () => DateTime.Now); + + Assert.AreEqual(3, l_oBikesTarget.Count, "One bike (nr. 5) got booked"); + + Assert.IsNotNull(l_oBikesTarget.GetById("5")); + Assert.AreEqual(InUseStateEnum.Booked, l_oBikesTarget.GetById("5").State.Value); + Assert.AreEqual("5", l_oBikesTarget.GetById("5").Id); + Assert.AreEqual(TypeOfBike.Cargo, l_oBikesTarget.GetById("5").TypeOfBike); + Assert.AreEqual(WheelType.Two, l_oBikesTarget.GetById("5").WheelType); + Assert.AreEqual(DateTime.Parse("2017 -11-28 14:08:32.756368+01"), l_oBikesTarget.GetById("5").State.From); // Sommer/ Winterzeit! + Assert.AreEqual("2360", l_oBikesTarget.GetById("5").State.Code); + } + + + [Test] + public void TestGetBikesAvailable() + { + var l_oBikesTarget = UpdaterJSON.GetBikesAvailable( + GetBikesAvailable(TinkApp.MerchantId, p_eSampleSet: SampleSets.Set2, p_lStageIndex: 1)); + + // Verify count of bikes + Assert.AreEqual(12, l_oBikesTarget.Count); + + // Verify properties of bike 5 + Assert.NotNull(l_oBikesTarget.GetById("5")); + Assert.AreEqual("5", l_oBikesTarget.GetById("5").Id); + Assert.AreEqual("TINK", l_oBikesTarget.GetById("5").Group.ToArray()[0]); + Assert.AreEqual(TypeOfBike.Cargo, l_oBikesTarget.GetById("5").TypeOfBike); + Assert.AreEqual(WheelType.Two, l_oBikesTarget.GetById("5").WheelType); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikesTarget.GetById("5").State.Value); + Assert.IsNull(l_oBikesTarget.GetById("5").State.From); // Sommer/ Winterzeit! + Assert.IsNull(l_oBikesTarget.GetById("5").State.Code); + + // Verify properties of bike 52 + Assert.NotNull(l_oBikesTarget.GetById("52")); + Assert.AreEqual("52", l_oBikesTarget.GetById("52").Id); + Assert.AreEqual("Konrad", l_oBikesTarget.GetById("52").Group.ToArray()[0]); + } + + [Test] + public void TestGetBikesOccupied() + { + var l_oBikesTarget = UpdaterJSON.GetBikesOccupied( + GetBikesOccupied(TinkApp.MerchantId, SampleSets.Set2, 1), + "a@b", + () => DateTime.Now); + + // Verify count of bikes + Assert.AreEqual(0, l_oBikesTarget.Count); + } + + [Test] + public void TestGetBikesAll_BikesAvaialbleResponse() + { + var availableResponse = JsonConvert.DeserializeObject( + @"{ + ""bikes"" : { + ""2352"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""1"", + ""gps"" : { ""latitude"": ""47.669888"", ""longitude"": ""9.167749"" }, + ""station"" : ""9"", + ""system"" : ""Ilockit"" + }, + ""2379"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""19"", + ""gps"" : { ""latitude"": ""47.6597846667"", ""longitude"": ""9.177439"" }, + ""station"" : ""3"" + } + } + }"); + + var bikes = UpdaterJSON.GetBikesAll( + availableResponse, + null, + "Heinz.Mustermann@posteo.de", + () => DateTime.Now); + + Assert.AreEqual( + 2, + bikes.Count); + + Assert.AreEqual( + 1, + bikes.Where(x => x is BikeInfo).Count(), + "There must be one ILockitBike and one BC bike (BikeInfo class)."); + } + [Test] + public void TestGetBikesAll_BikesAvaialbleResponse_InvalidState() + { + var availableResponse = JsonConvert.DeserializeObject( + @"{ + ""bikes"" : { + ""2352"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""reserved"", + ""bike"" : ""1"", + ""gps"" : { ""latitude"": ""47.669888"", ""longitude"": ""9.167749"" }, + ""station"" : ""9"" + } + } + }"); + + var bikes = UpdaterJSON.GetBikesAll( + availableResponse, + null, + "Heinz.Mustermann@posteo.de", + () => DateTime.Now); + + Assert.AreEqual( + 0, + bikes.Count, + "State of a element of BikesAvailableResponse must never be reserved."); + } + + [Test] + public void TestGetBikesAll_BikesAvaialbleResponse_InvalidStation() + { + var availableResponse = JsonConvert.DeserializeObject( + @"{ + ""bikes"" : { + ""2352"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""1"", + ""gps"" : { ""latitude"": ""47.669888"", ""longitude"": ""9.167749"" }, + ""station"" : """" + } + } + }"); + + var bikes = UpdaterJSON.GetBikesAll( + availableResponse, + null, + "Heinz.Mustermann@posteo.de", + () => DateTime.Now); + + Assert.AreEqual( + 0, + bikes.Count, + "Station of a element of BikesAvailableResponse must always be defined."); + } + + [Test] + public void TestGetBikesAll_BikesAvaialbleResponse_DuplicateId() + { + var availableResponse = JsonConvert.DeserializeObject( + @"{ + ""bikes"" : { + ""2352"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""1"", + ""gps"" : { ""latitude"": ""47.669888"", ""longitude"": ""9.167749"" }, + ""station"" : ""9"" + }, + ""2379"" : { + ""description"" : ""Cargo Long"", + ""state"" : ""available"", + ""bike"" : ""1"", + ""gps"" : { ""latitude"": ""47.6597846667"", ""longitude"": ""9.177439"" }, + ""station"" : ""3"" + } + } + }"); + + var bikes = UpdaterJSON.GetBikesAll( + availableResponse, + null, + "Heinz.Mustermann@posteo.de", + () => DateTime.Now); + + Assert.AreEqual( + 0, + bikes.Count, + "Ids of a elements of BikesAvailableResponse must be unique."); + } + + [Test] + public void TestGetBikesAll_BikesReservedOccupiedResponse_InvalidState() + { + var reservedOccupiedResponse = JsonConvert.DeserializeObject( + @"{ + ""bikes_occupied"" : { + ""87785"" : { + ""station"" : ""2"", + ""state"" : ""available"", + ""bike"" : ""20"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-12-01 22:21:57.740069+01"", + ""timeCode"" : ""6603"" + } + } + }"); + + var bikes = UpdaterJSON.GetBikesAll( + null, + reservedOccupiedResponse, + "Heinz.Mustermann@posteo.de", + () => DateTime.Now); + + Assert.AreEqual( + 0, + bikes.Count, + "State of a element of BikesAvailableResponse must never be reserved."); + } + + [Test] + public void TestGetBikesAll_BikesReservedOccupiedResponse_DuplicateId() + { + var reservedOccupiedResponse = JsonConvert.DeserializeObject( + @"{ + ""bikes_occupied"" : { + ""87785"" : { + ""station"" : ""2"", + ""state"" : ""occupied"", + ""bike"" : ""20"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-12-01 22:21:57.740069+01"", + ""timeCode"" : ""6603"" + }, + ""87782"" : { + ""station"" : ""4"", + ""state"" : ""occupied"", + ""bike"" : ""20"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-11-28 13:06:55.147368+01"", + ""timeCode"" : ""2931"" + } + } + }"); + + var bikes = UpdaterJSON.GetBikesAll( + null, + reservedOccupiedResponse, + "Heinz.Mustermann@posteo.de", + () => DateTime.Now); + + Assert.AreEqual( + 0, + bikes.Count, + "Ids of a elements of BikesAvailableResponse must be unique."); + } + + [Test] + public void TestGetBikesAll_BikesReservedOccupiedResponse() + { + var reservedOccupiedResponse = JsonConvert.DeserializeObject( + @"{ + ""bikes_occupied"" : { + ""87785"" : { + ""station"" : ""2"", + ""state"" : ""occupied"", + ""bike"" : ""20"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-12-01 22:21:57.740069+01"", + ""timeCode"" : ""6603"", + ""system"" : ""Ilockit"" + }, + ""87782"" : { + ""station"" : ""4"", + ""state"" : ""occupied"", + ""bike"" : ""7"", + ""description"" : ""Cargo Long"", + ""start_time"" : ""2017-11-28 13:06:55.147368+01"", + ""timeCode"" : ""2931"" + } + } + }"); + + var bikes = UpdaterJSON.GetBikesAll( + null, + reservedOccupiedResponse, + "Heinz.Mustermann@posteo.de", + () => DateTime.Now); + + Assert.AreEqual( + 2, + bikes.Count, + "Ids of a elements of BikesAvailableResponse must be unique."); + + Assert.AreEqual( + 1, + bikes.Where(x => x is BikeInfo).Count(), + "There must be one ILockitBike and one BC bike (BikeInfo class)."); + } + + [Test] + public void TestLoad_Reserved_CalculateAuthKeys() + { + // Construct requested bike. + var bike = new TINK.Model.Bike.BluetoothLock.BikeInfoMutable(new BikeInfo( + "17", + 22, + new Guid("0000f00d-1212-efde-1523-785fef13d123"), + new [] { (byte) 1, (byte)3, (byte)4 }, + new [] { (byte)11, (byte)3, (byte)1 }, + new [] { (byte)12, (byte)7, (byte)4 }, + DateTime.Now, + "a@b", + "1", + null, + null, + () => DateTime.Now)); + + var response = JsonConvert.DeserializeObject(@" + { + ""Ilockit_ID"": ""ISHAREIT+0815"", + ""state"": ""requested"", + ""start_time"": ""2018-01-27 17:33:00.989464+01"", + ""K_seed"": ""[-18, -80, 20, -90, 3, 69, 96, 4, -35, 75, -95, 102, 7, 121, -122, 15]"", + ""K_u"": ""[99, 104, 120, 121, 63, 99, -10, -110, 94, 70, 15, -112, -6, 101, 117, -90, -113, -54, -90, -95, 0, 0, 0, 0]"", + }"); + + // Update from new auth keys. + bike.Load(response, "a@b", () => DateTime.Now); + + // Verify that keys are correctly updated. + Assert.IsTrue(new byte[] { 256 - 18, 256 - 80, 20, 256 - 90, 3, 69, 96, 4, 256 - 35, 75, 256 - 95, 102, 7, 121, 256 - 122, 15 }.SequenceEqual(bike.LockInfo.Seed)); + Assert.IsTrue(new byte[] { 99, 104, 120, 121, 63, 99, 256 - 10, 256 - 110, 94, 70, 15, 256 - 112, 256 - 6, 101, 117, 256-90, 256 - 113, 256 - 54, 256 - 90, 256 - 95, 0, 0, 0, 0 }.SequenceEqual(bike.LockInfo.UserKey)); + } + + [Test] + public void TestLoad_Booked_CalculateAuthKeys() + { + // Construct occupied bike. + var bike = new TINK.Model.Bike.BluetoothLock.BikeInfoMutable(new BikeInfo( + "17", + 22, + new Guid("0000f00d-1212-efde-1523-785fef13d123"), + new[] { (byte)1, (byte)3, (byte)4 }, + new[] { (byte)11, (byte)3, (byte)1 }, + new[] { (byte)12, (byte)7, (byte)4 }, + DateTime.Now, + "a@b", + "1", + null /*operator uri*/)); + + var response = JsonConvert.DeserializeObject(@" + { + ""Ilockit_ID"": ""ISHAREIT+0815"", + ""state"": ""occupied"", + ""start_time"": ""2018-01-27 17:33:00.989464+01"", + ""K_seed"": ""[-18, -80, 20, -90, 3, 69, 96, 4, -35, 75, -95, 102, 7, 121, -122, 15]"", + ""K_u"": ""[99, 104, 120, 121, 63, 99, -10, -110, 94, 70, 15, -112, -6, 101, 117, -90, -113, -54, -90, -95, 0, 0, 0, 0]"", + }"); + + // Update from new auth keys. + bike.Load(response, "a@b", () => DateTime.Now); + + // Verify that keys are correctly updated. + Assert.IsTrue(new byte[] { 256 - 18, 256 - 80, 20, 256 - 90, 3, 69, 96, 4, 256 - 35, 75, 256 - 95, 102, 7, 121, 256 - 122, 15 }.SequenceEqual(bike.LockInfo.Seed)); + Assert.IsTrue(new byte[] { 99, 104, 120, 121, 63, 99, 256 - 10, 256 - 110, 94, 70, 15, 256 - 112, 256 - 6, 101, 117, 256 - 90, 256 - 113, 256 - 54, 256 - 90, 256 - 95, 0, 0, 0, 0 }.SequenceEqual(bike.LockInfo.UserKey)); + } + + [Test] + public void TestLoad_Reserved_DoBook() + { + // Construct requested bike. + var bike = new TINK.Model.Bike.BluetoothLock.BikeInfoMutable(new BikeInfo( + "17", + 22, + new Guid("0000f00d-1212-efde-1523-785fef13d123"), + new[] { (byte)1, (byte)3, (byte)4 }, + new[] { (byte)11, (byte)3, (byte)1 }, + new[] { (byte)12, (byte)7, (byte)4 }, + DateTime.Now, + "a@b", + "1", + null, + null, + () => DateTime.Now)); + + Assert.AreEqual(InUseStateEnum.Reserved, bike.State.Value); + + var response = JsonConvert.DeserializeObject(@" + { + ""Ilockit_ID"": ""ISHAREIT+0815"", + ""state"": ""occupied"", + ""start_time"": ""2018-01-27 17:33:00.989464+01"", + ""K_seed"": ""[-18, -80, 20, -90, 3, 69, 96, 4, -35, 75, -95, 102, 7, 121, -122, 15]"", + ""K_u"": ""[99, 104, 120, 121, 63, 99, -10, -110, 94, 70, 15, -112, -6, 101, 117, -90, -113, -54, -90, -95, 0, 0, 0, 0]"", + }"); + + // Update from new auth keys. + bike.Load(response, "a@b", () => DateTime.Now); + + Assert.AreEqual(InUseStateEnum.Booked, bike.State.Value); + } + + /// Verifies loading a default user without special permissions. + [Test] + public void TestGetAccount_DebugLevelNone() + { + var response = JsonConvert.DeserializeObject(@" + { + ""response_state"" : ""OK: nothing todo "", + ""user_group"" : [ ""300029"", ""300001"" ], + ""user_id"" : ""ohauff@posteo.de"", + ""authcookie"" : ""5781_f172cf59108fe53e7524c841847fee69_oiF2kahH"", + ""response"" : ""authorization"", + ""copri_version"" : ""4.1.0.0"", + ""response_text"" : ""Herzlich willkommen im Fahrradmietsystem"", + ""debuglevel"" : ""0"", + ""apiserver"" : ""https://shareeapp-fr01.copri.eu"" + }"); + + var account = response.GetAccount("merch123", "hallo@welt", "0815"); + + Assert.That( + account.DebugLevel, + Is.EqualTo(Permissions.None)); + } + + /// Verifies loading a admin user with all special permissions. + [Test] + public void TestGetAccount_DebugLevelAll() + { + var response = JsonConvert.DeserializeObject(@" + { + ""response_state"" : ""OK: nothing todo "", + ""user_group"" : [ ""300029"", ""300001"" ], + ""user_id"" : ""ohauff@posteo.de"", + ""authcookie"" : ""5781_f172cf59108fe53e7524c841847fee69_oiF2kahH"", + ""response"" : ""authorization"", + ""copri_version"" : ""4.1.0.0"", + ""response_text"" : ""Herzlich willkommen im Fahrradmietsystem"", + ""debuglevel"" : ""1"", + ""apiserver"" : ""https://shareeapp-fr01.copri.eu"" + }"); + + var account = response.GetAccount("merch123", "hallo@welt", "0815"); + + Assert.That( + account.DebugLevel, + Is.EqualTo(Permissions.All)); + } + + /// Verifies loading a admin user with all special permissions. + [Test] + public void TestGetAccount_DebugLevel_Logging() + { + var response = JsonConvert.DeserializeObject(@" + { + ""response_state"" : ""OK: nothing todo "", + ""user_group"" : [ ""300029"", ""300001"" ], + ""user_id"" : ""ohauff@posteo.de"", + ""authcookie"" : ""5781_f172cf59108fe53e7524c841847fee69_oiF2kahH"", + ""response"" : ""authorization"", + ""copri_version"" : ""4.1.0.0"", + ""response_text"" : ""Herzlich willkommen im Fahrradmietsystem"", + ""debuglevel"" : ""64"", + ""apiserver"" : ""https://shareeapp-fr01.copri.eu"" + }"); + + var account = response.GetAccount("merch123", "hallo@welt", "0815"); + + Assert.That( + account.DebugLevel, + Is.EqualTo(Permissions.PickLoggingLevel)); + } + + [Test] + public void TestCreateTariffDescription() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.50"", + ""abo_eur_per_month"" : ""920.99"", + ""free_hours"" : ""1.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.Create().Name, + Is.EqualTo("Tester Basic")); + + Assert.That( + tariffDescription.Create().Number, + Is.EqualTo(5494)); + + Assert.That( + tariffDescription.Create().FreeTimePerSession, + Is.EqualTo(TimeSpan.FromMinutes(90))); + + Assert.That( + tariffDescription.Create().FeeEuroPerHour, + Is.EqualTo(10.5)); + + Assert.That( + tariffDescription.Create().AboEuroPerMonth, + Is.EqualTo(920.99)); + } + + [Test] + public void TestCreateTariffDescription_Name() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.50"", + ""abo_eur_per_month"" : ""920.99"", + ""free_hours"" : ""1.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.Create().Name, + Is.EqualTo("Tester Basic")); + } + + [Test] + public void TestCreateTariffDescription_Number() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.50"", + ""abo_eur_per_month"" : ""920.99"", + ""free_hours"" : ""1.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.Create().Number, + Is.EqualTo(5494)); + } + + [Test] + public void TestCreateTariffDescription_FreeTimePerSession() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.50"", + ""abo_eur_per_month"" : ""920.99"", + ""free_hours"" : ""1.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.Create().FreeTimePerSession, + Is.EqualTo(TimeSpan.FromMinutes(90))); + } + + [Test] + public void TestCreateTariffDescription_FeeEuroPerHour() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.50"", + ""abo_eur_per_month"" : ""920.99"", + ""free_hours"" : ""1.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.Create().FeeEuroPerHour, + Is.EqualTo(10.5)); + } + + [Test] + public void TestCreateTariffDescription_AboEuroPerMonth() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.50"", + ""abo_eur_per_month"" : ""920.99"", + ""free_hours"" : ""1.50"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.Create().AboEuroPerMonth, + Is.EqualTo(920.99)); + } + + [Test] + public void TestCreateTariffDescription_Name_Empty() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{}"); + + Assert.That( + tariffDescription.Create().Name, + Is.Null); + } + + [Test] + public void TestCreateTariffDescription_Number_Empty() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{}"); + + Assert.That( + tariffDescription.Create().Number, + Is.Null); + } + + [Test] + public void TestCreateTariffDescription_FreeTimePerSession_Empty() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{}"); + + Assert.That( + tariffDescription.Create().FreeTimePerSession, + Is.EqualTo(TimeSpan.Zero)); + } + + [Test] + public void TestCreateTariffDescription_FeeEuroPerHour_Empty() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{}"); + + Assert.That( + tariffDescription.Create().FeeEuroPerHour, + Is.NaN); + } + + [Test] + public void TestCreateTariffDescription_AboEuroPerMonth_Empty() + { + var tariffDescription = TINK.Repository.Response.JsonConvertRethrow.DeserializeObject( + @"{}"); + + Assert.That( + tariffDescription.Create().AboEuroPerMonth, + Is.NaN); + } + + [Test] + public void TestCreateTariffDescription_Name_Null() + { + Assert.That( + BikeInfoFactory.Create((TINK.Repository.Response.TariffDescription)null).Name, + Is.Null); + } + + [Test] + public void TestCreateTariffDescription_Number_Null() + { + Assert.That( + BikeInfoFactory.Create((TINK.Repository.Response.TariffDescription)null).Number, + Is.Null); + } + + [Test] + public void TestCreateTariffDescription_FreeTimePerSession_Null() + { + Assert.That( + BikeInfoFactory.Create((TINK.Repository.Response.TariffDescription)null).FreeTimePerSession, + Is.EqualTo(TimeSpan.Zero)); + } + + [Test] + public void TestCreateTariffDescription_FeeEuroPerHour_Null() + { + Assert.That( + BikeInfoFactory.Create((TINK.Repository.Response.TariffDescription)null).FeeEuroPerHour, + Is.NaN); + } + + [Test] + public void TestCreateTariffDescription_AboEuroPerMonth_Null() + { + Assert.That( + BikeInfoFactory.Create((TINK.Repository.Response.TariffDescription)null).AboEuroPerMonth, + Is.NaN); + } + } +} diff --git a/TestShareeLib/Model/TestBikeCollectionFilter.cs b/TestShareeLib/Model/TestBikeCollectionFilter.cs index d650664..8a84914 100644 --- a/TestShareeLib/Model/TestBikeCollectionFilter.cs +++ b/TestShareeLib/Model/TestBikeCollectionFilter.cs @@ -13,16 +13,16 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike public void TestGetAtStation() { var coll = new BikeCollection( - new Dictionary + new Dictionary { - {3, new TINK.Model.Bike.BC.BikeInfo(7, 3 /* Stadion id */) }, - {7, new TINK.Model.Bike.BC.BikeInfo(8, 12 /* Stadion id */) }, - {12, new TINK.Model.Bike.BC.BikeInfo(33, 12 /* Stadion id */) } + {"3", new TINK.Model.Bike.BC.BikeInfo("7", "3" /* Stadion id */) }, + {"7", new TINK.Model.Bike.BC.BikeInfo("8", "12" /* Stadion id */) }, + {"12", new TINK.Model.Bike.BC.BikeInfo("33", "12" /* Stadion id */) } }); Assert.AreEqual( 0, - BikeCollectionFilter.GetAtStation(null, 12).Count); + BikeCollectionFilter.GetAtStation(null, "12").Count); Assert.AreEqual( 0, @@ -30,15 +30,15 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike Assert.AreEqual( 0, - coll.GetAtStation(22).Count); + coll.GetAtStation("22").Count); Assert.AreEqual( 1, - coll.GetAtStation(3).Count); + coll.GetAtStation("3").Count); Assert.AreEqual( 2, - coll.GetAtStation(12).Count); + coll.GetAtStation("12").Count); } [Test] @@ -54,10 +54,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike { // Holds no LockIt bikes var coll = new BikeCollection( - new Dictionary + new Dictionary { - {7, new TINK.Model.Bike.BC.BikeInfo(8, 12 /* Stadion id */) }, - {12, new TINK.Model.Bike.BC.BikeInfo(33, 12 /* Stadion id */) } + {"7", new TINK.Model.Bike.BC.BikeInfo("8", "12" /* Stadion id */) }, + {"12", new TINK.Model.Bike.BC.BikeInfo("33", "12" /* Stadion id */) } }); Assert.AreEqual( @@ -71,11 +71,11 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike // Holds no LockIt bike with matching station number. var coll = new BikeCollection( - new Dictionary + new Dictionary { - {7, new TINK.Model.Bike.BC.BikeInfo(8, 12 /* Stadion id */) }, - {11, new TINK.Model.Bike.BluetoothLock.BikeInfo(33, 5200544, new Guid("00000000-0000-0000-0000-000000000001"),12 /* Stadion id */) }, - {12, new TINK.Model.Bike.BC.BikeInfo(33, 12 /* Stadion id */) } + {"7", new TINK.Model.Bike.BC.BikeInfo("8", "12" /* Stadion id */) }, + {"11", new TINK.Model.Bike.BluetoothLock.BikeInfo("33", 5200544, new Guid("00000000-0000-0000-0000-000000000001"), "12" /* Stadion id */) }, + {"12", new TINK.Model.Bike.BC.BikeInfo("33", "12" /* Stadion id */) } }); Assert.AreEqual( diff --git a/TestShareeLib/Model/TestTinkApp.cs b/TestShareeLib/Model/TestTinkApp.cs new file mode 100644 index 0000000..7d1b8bf --- /dev/null +++ b/TestShareeLib/Model/TestTinkApp.cs @@ -0,0 +1,74 @@ +using NUnit.Framework; +using System; +using TINK.Model; +using TINK.Model.Connector; +using TINK.Repository; +using TINK.Model.Services.CopriApi.ServerUris; +using static TINK.Repository.CopriCallsMemory; +using TINK.Services; +using NSubstitute; +using TINK.Model.Services.Geolocation; +using TINK.Services.BluetoothLock; +using TINK.Model.Device; +using TINK.Model.User.Account; +using Plugin.Permissions.Abstractions; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace TestTINKLib.Fixtures.UseCases.Logout +{ + [TestFixture] + public class TestTinkApp + { + [Test] + public void Logout() + { + var accountStore = Substitute.For(); + var locksService = Substitute.For(); + var device = Substitute.For(); + var specialFolder = Substitute.For(); + var permissions = Substitute.For(); + var account = Substitute.For(); + + accountStore.Load().Returns(account); + account.Mail.Returns("javaminister@gmail.com"); + account.Pwd.Returns("javaminister"); + account.SessionCookie.Returns("4da3044c8657a04ba60e2eaa753bc51a"); + account.Group.Returns(new List { "TINK" }); + + // No user logged in is initial state to verify. + var l_oTinkApp = new TinkApp( + new TINK.Model.Settings.Settings( + activeLockService: locksService.GetType().FullName, + activeGeolocationService: "NotRelevantActiveGeoloactionServiceName", + activeUri: new Uri(CopriServerUriList.TINK_DEVEL)), + accountStore, + (isConnected, uri, sessionCookie, mail, expiresAfter) => string.IsNullOrEmpty(sessionCookie) + ? new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1)) + : new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1, sessionCookie)), + Substitute.For>(), + locksService, + device, + specialFolder, + null, // Cipher + permissions, + isConnectedFunc: () => true, + currentVersion: new Version(3, 2, 0, 115), + lastVersion: new Version(3, 0, 173)); // Current app version. Must be larger or equal 3.0.173 to + + Assert.IsTrue(l_oTinkApp.ActiveUser.IsLoggedIn); + Assert.AreEqual(13, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count); + Assert.AreEqual(2, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count); + Assert.AreEqual("4da3044c8657a04ba60e2eaa753bc51a", l_oTinkApp.GetConnector(true).Command.SessionCookie); + + // Log user out. + l_oTinkApp.GetConnector(true).Command.DoLogout().Wait(); + l_oTinkApp.ActiveUser.Logout(); + + Assert.IsFalse(l_oTinkApp.ActiveUser.IsLoggedIn); + Assert.AreEqual(11, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count); + Assert.AreEqual(0, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count); + Assert.IsNull(l_oTinkApp.GetConnector(true).Command.SessionCookie); + } + } +} diff --git a/TestShareeLib/Model/TestTinkAppLogin.cs b/TestShareeLib/Model/TestTinkAppLogin.cs new file mode 100644 index 0000000..5c35e43 --- /dev/null +++ b/TestShareeLib/Model/TestTinkAppLogin.cs @@ -0,0 +1,67 @@ +using NUnit.Framework; +using System; +using TINK.Model; +using TINK.Model.Connector; +using TINK.Repository; +using TINK.Model.Services.CopriApi.ServerUris; +using static TINK.Repository.CopriCallsMemory; +using TINK.Services; +using NSubstitute; +using TINK.Model.Services.Geolocation; +using TINK.Services.BluetoothLock; +using TINK.Model.Device; +using TINK.Model.User.Account; +using Plugin.Permissions.Abstractions; +using System.Threading.Tasks; + +namespace TestTINKLib.Fixtures.UseCases.Login +{ + + [TestFixture] + public class TestTinkApp + { + [Test] + public async Task Login() + { + var accountStore = Substitute.For(); + var locksService = Substitute.For(); + var device = Substitute.For(); + var specialFolder = Substitute.For(); + var permissions = Substitute.For(); + + // No user logged in is initial state to verify. + var l_oTinkApp = new TinkApp( + new TINK.Model.Settings.Settings( + activeLockService: locksService.GetType().FullName, + activeGeolocationService: "NotRelevantActiveGeoloactionServiceName", + activeUri: new Uri(CopriServerUriList.TINK_DEVEL)), + accountStore, + (isConnected, uri, sessionCookie, mail, expiresAfter) => string.IsNullOrEmpty(sessionCookie) + ? new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1)) as IConnector + : new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1, sessionCookie)), + Substitute.For>(), + locksService, + device, + specialFolder, + null, // Cipher + permissions, + isConnectedFunc: () => true, + currentVersion: new Version(3, 2, 0, 115), + lastVersion: new Version(3, 0, 173) /* Current app version. Must be larger or equal 3.0.173 to lastVersion*/); + + Assert.IsFalse(l_oTinkApp.ActiveUser.IsLoggedIn); + Assert.AreEqual(11, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count); + Assert.AreEqual(0, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count); + Assert.IsNull(l_oTinkApp.GetConnector(true).Command.SessionCookie); + + // Log user out. + var l_oAccount = l_oTinkApp.GetConnector(true).Command.DoLogin("javaminister@gmail.com", "javaminister", "HwId1000000000000").Result; + await l_oTinkApp.ActiveUser.Login(l_oAccount); + + Assert.IsTrue(l_oTinkApp.ActiveUser.IsLoggedIn); + Assert.AreEqual(13, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count); + Assert.AreEqual(2, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count); + Assert.AreEqual("4da3044c8657a04ba60e2eaa753bc51a", l_oTinkApp.GetConnector(true).Command.SessionCookie); + } + } +} diff --git a/TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs b/TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs index e912a20..28a2300 100644 --- a/TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs +++ b/TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; using NUnit.Framework; -using TINK.Model.Repository.Response; +using TINK.Repository.Response; namespace TestTINKLib.Fixtures.ObjectTests.Bike { @@ -20,19 +20,21 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike ""bike"" : ""231"", ""description"" : ""Stadtrad"", ""system"" : ""BC"", - ""bike_group"" : ""Konrad"", + ""bike_group"" : [ ""Konrad"" ], ""station"" : """", ""state"" : ""available"", - ""gps"" : ""9.1594501, 47.6749928"" + ""gps"" : { ""latitude"": ""9.1594501"", ""longitude"": ""47.6749928"" } } }, - ""copri_version"" : ""3.0.0.0"", + ""copri_version"" : ""4.1.0.0"", ""authcookie"" : """", ""response_state"" : ""OK"" } "); - Assert.IsNull(l_oBikes.bikes[231].station); + Assert.That( + l_oBikes.bikes["231"].station, + Is.Empty); } [Test] @@ -48,18 +50,18 @@ namespace TestTINKLib.Fixtures.ObjectTests.Bike ""bike"" : ""231"", ""description"" : ""Stadtrad"", ""system"" : ""BC"", - ""bike_group"" : ""Konrad"", + ""bike_group"" : [ ""Konrad"" ], ""state"" : ""available"", - ""gps"" : ""9.1594501, 47.6749928"" + ""gps"" : { ""latitude"": ""9.1594501"", ""longitude"": ""47.6749928"" } } }, - ""copri_version"" : ""3.0.0.0"", + ""copri_version"" : ""4.1.0.0"", ""authcookie"" : """", ""response_state"" : ""OK"" } "); - Assert.IsNull(l_oBikes.bikes[231].station); + Assert.IsNull(l_oBikes.bikes["231"].station); } } diff --git a/TestShareeLib/Repository/Response/TestJsonConvert.cs b/TestShareeLib/Repository/Response/TestJsonConvert.cs index b3606c4..86e9428 100644 --- a/TestShareeLib/Repository/Response/TestJsonConvert.cs +++ b/TestShareeLib/Repository/Response/TestJsonConvert.cs @@ -11,7 +11,7 @@ namespace TestShareeLib.Model.Repository.Response public void TestDeserializeObject() { Assert.That( - () => JsonConvert.DeserializeObject( + () => JsonConvertRethrow.DeserializeObject( @"{ ""eur_per_hour"" : ""10.00"", ""abo_eur_per_month"" : ""920.00"", diff --git a/TestShareeLib/Repository/Response/TestTariffDescription.cs b/TestShareeLib/Repository/Response/TestTariffDescription.cs index 6e80a7b..918d4aa 100644 --- a/TestShareeLib/Repository/Response/TestTariffDescription.cs +++ b/TestShareeLib/Repository/Response/TestTariffDescription.cs @@ -9,7 +9,7 @@ namespace TestShareeLib.Repository.Response [Test] public void TestDeserialize() { - var tariffDescription = JsonConvert.DeserializeObject( + var tariffDescription = JsonConvertRethrow.DeserializeObject( @"{ ""eur_per_hour"" : ""10.00"", ""abo_eur_per_month"" : ""920.00"", @@ -45,7 +45,7 @@ namespace TestShareeLib.Repository.Response [Test] public void TestDeserialize_Missing() { - var tariffDescription = JsonConvert.DeserializeObject( + var tariffDescription = JsonConvertRethrow.DeserializeObject( @"{ ""number"" : ""5494"", ""name"" : ""Tester Basic"" @@ -78,7 +78,7 @@ namespace TestShareeLib.Repository.Response [Test] public void TestDeserialize_NewElement() { - var tariffDescription = JsonConvert.DeserializeObject( + var tariffDescription = JsonConvertRethrow.DeserializeObject( @"{ ""number"" : ""5494"", ""name"" : ""Tester Basic"", diff --git a/TestShareeLib/Repository/TestCopriCallsHttps.cs b/TestShareeLib/Repository/TestCopriCallsHttps.cs new file mode 100644 index 0000000..c7434aa --- /dev/null +++ b/TestShareeLib/Repository/TestCopriCallsHttps.cs @@ -0,0 +1,17 @@ +using NUnit.Framework; + +namespace UITest.Fixtures.Connector +{ + [TestFixture] + public class TestCopriCallsHttps + { + public const string CATEGORY_REQUIRESCOPRI = "RequiresCOPRI"; + + public const string CATEGORY_USESLIVESERVER = "RequiresCOPRI.Live"; + + public const string CATEGORY_USESDEVELSERVER = "RequiresCOPRI.Devel"; + + public const string TESTAGENT = "TestShareeLib"; + + } +} diff --git a/TestShareeLib/Repository/TestCopriCallsMonkeyStore.cs b/TestShareeLib/Repository/TestCopriCallsMonkeyStore.cs new file mode 100644 index 0000000..ef5ecb3 --- /dev/null +++ b/TestShareeLib/Repository/TestCopriCallsMonkeyStore.cs @@ -0,0 +1,83 @@ +using MonkeyCache.FileStore; +using Newtonsoft.Json; +using NUnit.Framework; +using System.Threading.Tasks; +using TINK.Repository; +using TINK.Repository.Response; + +namespace UITest.Fixtures.ObjectTests.Connector +{ + [TestFixture] + public class TestCopriCallsMonkeyStore + { + [Test] + public void TestConstruct() + { + var bikesAvailable = JsonConvert.DeserializeObject(CopriCallsMonkeyStore.BIKESAVAILABLE); + Assert.NotNull(bikesAvailable?.bikes); + + var bikesOccupied = JsonConvert.DeserializeObject(CopriCallsMonkeyStore.BIKESOCCUPIED); + Assert.NotNull(bikesOccupied?.bikes_occupied); + + var stations = JsonConvert.DeserializeObject(CopriCallsMonkeyStore.STATIONSALL); + Assert.NotNull(stations?.stations); + } + + [Test] + public async Task TestGetStations() + { + if (string.IsNullOrEmpty(Barrel.ApplicationId)) + { + Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + } + + try + { + var cache = new CopriCallsMonkeyStore("123456789"); + Assert.AreEqual(0, (await cache.GetStationsAsync()).stations.Count); + } + finally + { + Barrel.Current.EmptyAll(); + } + } + + [Test] + public async Task TestGetBikesAvailable() + { + if (string.IsNullOrEmpty(Barrel.ApplicationId)) + { + Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + } + + try + { + var cache = new CopriCallsMonkeyStore("123456789"); + Assert.AreEqual(0, (await cache.GetBikesAvailableAsync()).bikes.Count); + } + finally + { + Barrel.Current.EmptyAll(); + } + } + + [Test] + public async Task TestGetBikesOccupied() + { + if (string.IsNullOrEmpty(Barrel.ApplicationId)) + { + Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + } + + try + { + var cache = new CopriCallsMonkeyStore("123456789", "abc"); + Assert.AreEqual(0, (await cache.GetBikesOccupiedAsync()).bikes_occupied.Count); + } + finally + { + Barrel.Current.EmptyAll(); + } + } + } +} diff --git a/TestShareeLib/Repository/TestCopriCallsStatic.cs b/TestShareeLib/Repository/TestCopriCallsStatic.cs new file mode 100644 index 0000000..2bcca04 --- /dev/null +++ b/TestShareeLib/Repository/TestCopriCallsStatic.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json; +using NUnit.Framework; +using TINK.Repository.Response; +using TINK.Repository; + +namespace TestShareeLib.Repository +{ + [TestFixture] + public class TestCopriCallsStatic + { + [Test] + public void TestDeserializeResponse_Factory_SupportedVersion() + { + var response = @" + { + ""shareejson"" : + { + ""copri_version"" : ""4.1.5.7"" + } + }"; + + Assert.That( + response.DeserializeResponse(version => new ResponseBase()).copri_version, + Is.EqualTo("4.1.5.7")); + } + + [Test] + public void TestDeserializeResponse_Factory_UnspportedVersion() + { + // Future version + var response = @" + { + ""shareejson"" : + { + ""copri_version"" : ""4.2.5.7"" + } + }"; + + var factoryObject = JsonConvert.DeserializeObject>( + @"{ + ""shareejson"" : + { + ""copri_version"" : ""9.9.9.9"" + } + }"); + + Assert.That( + response.DeserializeResponse(version => factoryObject).shareejson.copri_version, + Is.EqualTo("9.9.9.9")); + } + + [Test] + public void TestDeserializeResponse_Exception_SupportedVersion() + { + // Future version + var response = @" + { + ""shareejson"" : + { + ""copri_version"" : ""4.1.5.7"" + } + }"; + + Assert.That( + response.DeserializeResponse(version => new System.Exception("hello")).copri_version, + Is.EqualTo("4.1.5.7"), + "No exception must be thrown because version 4.1.x is supported."); + } + + [Test] + public void TestDeserializeResponse_Exception_UnspportedVersion() + { + var response = @" + { + ""shareejson"" : + { + ""copri_version"" : ""4.2.5.7"" + } + }"; + + Assert.That( + () => response.DeserializeResponse(version => new System.Exception("Ho")).copri_version, + Throws.InstanceOf()); + } + } +} diff --git a/TestShareeLib/TestShareeLib.csproj b/TestShareeLib/TestShareeLib.csproj index cfca1d6..75f02d6 100644 --- a/TestShareeLib/TestShareeLib.csproj +++ b/TestShareeLib/TestShareeLib.csproj @@ -27,17 +27,14 @@ - - - + + + + - - - - diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs index 96edea6..e5eb8a8 100644 --- a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using TINK.Model.Bike.BluetoothLock; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.Tdo; @@ -16,10 +16,10 @@ using TINK.ViewModel; using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using TINK.Model.User; -using TINK.Model.Repository.Request; -using TINK.Model.Repository.Response; -using TINK.Repository.Exception; +using TINK.Repository.Request; +using TINK.Repository.Response; using Newtonsoft.Json; +using TINK.Model.Device; namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -40,6 +40,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler Substitute.For(), Substitute.For(), () => Substitute.For(), + Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For()); @@ -75,6 +76,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -95,7 +97,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Updating..."; @@ -135,6 +137,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -156,7 +159,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Lock cannot be opened until bike is near.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Lock cannot be opened until bike is near.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -194,12 +197,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() - .Returns>(x => throw new CouldntOpenBoldBlockedException()); + .Returns>(x => throw new CouldntOpenBoldIsBlockedException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); @@ -215,7 +219,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -253,6 +257,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -274,7 +279,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "After try to open lock state closed is reported.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "After try to open lock state closed is reported.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -312,12 +317,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() - .Returns>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Unknown)); + .Returns>(x => throw new CouldntOpenBoldWasBlockedException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); @@ -333,7 +339,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Lock reports unknown bold position.", "OK"); + viewService.DisplayAlert("Lock can not be opened!", "Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -371,6 +377,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -392,7 +399,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Exception message.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Exception message.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -406,6 +413,135 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler Assert.IsTrue(subsequent.IsLockitButtonVisible); } + /// + /// Use case: Opens lock + /// Final state: Booked opened. + /// + [Test] + public void TestOpenGetBatteryPercentageAsyncThrowsOutOfReachException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + Substitute.For(), + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + locks[0].GetBatteryPercentageAsync().Returns>(x => throw new OutOfReachException()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[0].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Battery status can only be read when bike is nearby."; + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + + /// + /// Use case: Opens lock + /// Final state: Booked opened. + /// + [Test] + public void TestOpenGetBatteryPercentageAsyncThrowsExcepton() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + Substitute.For(), + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + locks[0].GetBatteryPercentageAsync().Returns>(x => throw new Exception()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.ActionText = "One moment please..."; + pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[0].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Battery status cannot be read."; + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + /// /// Use case: Open lock /// Final state: Booked open. @@ -430,6 +566,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -453,7 +590,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "No web error on updating locking status."; @@ -494,6 +631,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -517,7 +655,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Connection error on updating locking status."; @@ -558,6 +696,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -582,7 +721,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Status error on updating lock state."; @@ -623,6 +762,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -681,6 +821,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -740,6 +881,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -799,6 +941,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -860,6 +1003,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -921,6 +1065,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs index 449f23f..c7cca46 100644 --- a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs @@ -6,8 +6,8 @@ using System.Threading.Tasks; using TINK.Model.Bike.BluetoothLock; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; -using TINK.Model.Repository.Response; +using TINK.Repository.Exception; +using TINK.Repository.Response; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.Tdo; @@ -18,8 +18,8 @@ using TINK.ViewModel; using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using TINK.Model.User; -using TINK.Model.Repository.Request; -using TINK.Repository.Exception; +using TINK.Repository.Request; +using TINK.Model.Device; namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -39,6 +39,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler Substitute.For(), Substitute.For(), () => Substitute.For(), + Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For()); @@ -75,10 +76,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(false)); var subsequent = handler.HandleRequestOption1().Result; @@ -124,10 +128,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Disconnected); // Requsthandler factory queries lock state to create appropriate request handler object. @@ -182,17 +189,20 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); var response = JsonConvert.DeserializeObject(@" { ""response"" : ""authorization"", ""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51a"", - ""user_group"" : ""TINK,Konrad"", + ""user_group"" : [ ""TINK"", ""Konrad"" ], ""response_state"" : ""OK"", ""apiserver"" : ""https://tinkwwp.copri-bike.de"" }"); @@ -253,10 +263,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub"))); @@ -315,10 +328,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.")); @@ -377,6 +393,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -424,10 +441,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -448,7 +468,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Updating..."; @@ -489,10 +509,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. @@ -554,10 +577,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. @@ -619,10 +645,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -644,7 +673,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Lock cannot be opened until bike is near.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Lock cannot be opened until bike is near.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -683,16 +712,19 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns>(x => throw new Exception("Exception message.")); // Return lock state indicating success - bike.State.Value.Returns(TINK.Model.State.InUseStateEnum.Reserved); // Booking call leads to setting of state to booked. + bike.State.Value.Returns(InUseStateEnum.Reserved); // Booking call leads to setting of state to booked. bike.LockInfo.State.Returns(LockingState.Unknown); var subsequent = handler.HandleRequestOption2().Result; @@ -708,7 +740,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Exception message.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Exception message.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -747,10 +779,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -808,10 +843,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -834,7 +872,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Battery status can only be read when bike is nearby."; bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); @@ -876,10 +914,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -902,7 +943,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Battery status cannot be read."; bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); @@ -944,10 +985,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -970,7 +1014,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "No web error on updating locking status."; @@ -1012,10 +1056,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -1038,7 +1085,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Connection error on updating locking status."; @@ -1080,10 +1127,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); + bike.Id.Returns("0"); + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() @@ -1107,7 +1157,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Status error on updating lock state."; diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs index d174a43..4ee9fcc 100644 --- a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using TINK.Model.Bike.BluetoothLock; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.Connector; -using TINK.Model.Repository.Exception; +using TINK.Repository.Exception; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.Tdo; @@ -16,10 +16,10 @@ using TINK.ViewModel; using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using TINK.Model.User; -using TINK.Model.Repository.Request; -using TINK.Model.Repository.Response; -using TINK.Repository.Exception; +using TINK.Repository.Request; +using TINK.Repository.Response; using Newtonsoft.Json; +using TINK.Model.Device; namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { @@ -42,6 +42,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler Substitute.For(), Substitute.For(), () => Substitute.For(), + Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For()); @@ -77,6 +78,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -97,7 +99,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Updating..."; @@ -137,6 +139,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -158,7 +161,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Lock cannot be opened until bike is near.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Lock cannot be opened until bike is near.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -196,12 +199,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() - .Returns>(x => throw new CouldntOpenBoldBlockedException()); + .Returns>(x => throw new CouldntOpenBoldIsBlockedException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); @@ -217,7 +221,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -255,6 +259,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -276,7 +281,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "After try to open lock state closed is reported.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "After try to open lock state closed is reported.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -314,12 +319,13 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() - .Returns>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Unknown)); + .Returns>(x => throw new CouldntOpenBoldWasBlockedException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); @@ -335,7 +341,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Lock reports unknown bold position.", "OK"); + viewService.DisplayAlert("Lock can not be opened!", "Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -373,6 +379,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -394,7 +401,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; - viewService.DisplayAlert("Lock can not be opened!", "Exception message.", "OK"); + viewService.DisplayAlert("Error while opening lock!", "Exception message.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; @@ -432,6 +439,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -455,7 +463,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "No web error on updating locking status."; @@ -496,6 +504,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -519,7 +528,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Connection error on updating locking status."; @@ -560,6 +569,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -584,7 +594,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler bikesViewModel.ActionText = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; - locks[bike.Id].GetBatteryPercentageAsync(); + locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Status error on updating lock state."; @@ -625,6 +635,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -679,6 +690,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -738,6 +750,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -798,6 +811,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -859,6 +873,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); @@ -920,6 +935,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler geolocation, locks, () => pollingManager, + Substitute.For(), viewService, bikesViewModel, activeUser); diff --git a/TestShareeLib/ViewModel/TestViewModelHelper.cs b/TestShareeLib/ViewModel/TestViewModelHelper.cs new file mode 100644 index 0000000..c6f21fd --- /dev/null +++ b/TestShareeLib/ViewModel/TestViewModelHelper.cs @@ -0,0 +1,95 @@ +using NSubstitute; +using NUnit.Framework; +using TINK.Model.Bikes.Bike.BC; +using TINK.ViewModel; + +namespace TestShareeLib.ViewModel +{ + [TestFixture] + public class TestViewModelHelper + { + [Test] + public void TestGetFullDisplayName() + { + var bike = Substitute.For(); + bike.Description.Returns("MyStation"); + bike.Id.Returns("MyId"); + + Assert.That( + bike.GetFullDisplayName(), + Is.EqualTo("MyStation, Nr. MyId")); + } + + [Test] + public void TestGetFullDisplayNameIdEmpty() + { + var bike = Substitute.For(); + bike.Description.Returns("MyStation"); + bike.Id.Returns(""); + + Assert.That( + bike.GetFullDisplayName(), + Is.EqualTo("MyStation, Nr. ")); + } + + [Test] + public void TestGetFullDisplayNameEmpty() + { + var bike = Substitute.For(); + bike.Description.Returns(""); + bike.Id.Returns("Id33"); + + Assert.That( + bike.GetDisplayName(), + Is.EqualTo("Id33")); + } + + [Test] + public void TestGetDisplayName() + { + var bike = Substitute.For(); + bike.Description.Returns("MyStation"); + bike.Id.Returns("Id33"); + + Assert.That( + bike.GetDisplayName(), + Is.EqualTo("MyStation")); + } + + [Test] + public void TestGetDisplayNameEmpty() + { + var bike = Substitute.For(); + bike.Description.Returns(""); + bike.Id.Returns("Id33"); + + Assert.That( + bike.GetDisplayName(), + Is.EqualTo("Id33")); + } + + [Test] + public void TestGetDisplayId() + { + var bike = Substitute.For(); + bike.Description.Returns("ValidName"); + bike.Id.Returns("Id33"); + + Assert.That( + bike.GetDisplayId(), + Is.EqualTo("Id33")); + } + + [Test] + public void TestGetDisplayIdNameEmpty() + { + var bike = Substitute.For(); + bike.Description.Returns(""); + bike.Id.Returns("Id33"); + + Assert.That( + bike.GetDisplayId(), + Is.EqualTo("")); + } + } +}