mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-12-22 23:26:31 +01:00
Select station page added to ease getting operator specific contact information.
This commit is contained in:
parent
a58c33f005
commit
ddfea49ea6
24 changed files with 1105 additions and 89 deletions
5
TINK.sln
5
TINK.sln
|
@ -24,11 +24,6 @@ EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestLockItBLE", "TestLockItBLE\TestLockItBLE.csproj", "{2581E9AD-4F56-431A-AB87-1B6D80D546AA}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestLockItBLE", "TestLockItBLE\TestLockItBLE.csproj", "{2581E9AD-4F56-431A-AB87-1B6D80D546AA}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
|
||||||
TINK\TINK\TINK.projitems*{5297504f-603f-4e1a-98aa-57c4a0d9d833}*SharedItemsImports = 13
|
|
||||||
TINK\TINK\TINK.projitems*{62b8950a-70b8-4f9d-affc-0a1ebe7bc9e7}*SharedItemsImports = 4
|
|
||||||
TINK\TINK\TINK.projitems*{f2d8208f-a8bf-4403-b0ae-2a1d270e4dc9}*SharedItemsImports = 4
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||||
Ad-Hoc|ARM = Ad-Hoc|ARM
|
Ad-Hoc|ARM = Ad-Hoc|ARM
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.241" android:versionCode="241">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.242" android:versionCode="242">
|
||||||
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="30" />
|
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="30" />
|
||||||
<!-- Google Maps related permissions -->
|
<!-- Google Maps related permissions -->
|
||||||
<permission android:name="com.ecs.google.maps.v2.actionbarsherlock.permission.MAPS_RECEIVE" android:protectionLevel="signature" />
|
<permission android:name="com.ecs.google.maps.v2.actionbarsherlock.permission.MAPS_RECEIVE" android:protectionLevel="signature" />
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace TINK.iOS
|
||||||
e.NativeView.AccessibilityIdentifier = e.View.AutomationId;
|
e.NativeView.AccessibilityIdentifier = e.View.AutomationId;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
LoadApplication (new TINK.App ());
|
LoadApplication (new App ());
|
||||||
|
|
||||||
// Required for initialization of Maps, see https://developer.xamarin.com/guides/xamarin-forms/user-interface/map/
|
// Required for initialization of Maps, see https://developer.xamarin.com/guides/xamarin-forms/user-interface/map/
|
||||||
Xamarin.FormsGoogleMaps.Init("000000000000000000000000000000000000000");
|
Xamarin.FormsGoogleMaps.Init("000000000000000000000000000000000000000");
|
||||||
|
|
|
@ -49,8 +49,8 @@
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>sharee.bike</string>
|
<string>sharee.bike</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>241</string>
|
<string>242</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>3.0.241</string>
|
<string>3.0.242</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -30,6 +30,10 @@
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)View\BoolInverterConverter.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)View\BoolInverterConverter.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)View\Contact\SelectStationPage.xaml.cs">
|
||||||
|
<DependentUpon>SelectStationPage.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)View\FeedbackPopup.xaml.cs">
|
<Compile Include="$(MSBuildThisFileDirectory)View\FeedbackPopup.xaml.cs">
|
||||||
<DependentUpon>FeedbackPopup.xaml</DependentUpon>
|
<DependentUpon>FeedbackPopup.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
|
@ -325,4 +329,10 @@
|
||||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)View\Contact\SelectStationPage.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -4,7 +4,7 @@
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
|
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
|
||||||
x:Class="TINK.View.Contact.ContactPage"
|
x:Class="TINK.View.Contact.ContactPage"
|
||||||
Title="Kontakt">
|
Title="{x:Static resources:AppResources.MarkingContactPageTitle}">
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<conv:StringNotNullOrEmptyToVisibleConverter x:Key="StringNotNullOrEmpty_Converter"/>
|
<conv:StringNotNullOrEmptyToVisibleConverter x:Key="StringNotNullOrEmpty_Converter"/>
|
||||||
<conv:BoolInverterConverter x:Key="BoolInvert_Converter"/>
|
<conv:BoolInverterConverter x:Key="BoolInvert_Converter"/>
|
||||||
|
@ -17,9 +17,14 @@
|
||||||
IsVisible="{Binding
|
IsVisible="{Binding
|
||||||
Path=IsOperatorInfoAvaliable,
|
Path=IsOperatorInfoAvaliable,
|
||||||
Converter={StaticResource BoolInvert_Converter}}">
|
Converter={StaticResource BoolInvert_Converter}}">
|
||||||
|
<StackLayout>
|
||||||
<Label
|
<Label
|
||||||
TextType="Html"
|
TextType="Html"
|
||||||
Text="{x:Static resources:AppResources.MarkingContactNoStationInfoAvailableNoButton}"/>
|
Text="{x:Static resources:AppResources.MarkingContactNoStationInfoAvailableNoButton}"/>
|
||||||
|
<Button
|
||||||
|
Text="{x:Static resources:AppResources.ActionSelectStation}"
|
||||||
|
Command="{Binding OnSelectStationRequest}"/>
|
||||||
|
</StackLayout>
|
||||||
</Frame>
|
</Frame>
|
||||||
<Frame
|
<Frame
|
||||||
IsVisible="{Binding IsOperatorInfoAvaliable}">
|
IsVisible="{Binding IsOperatorInfoAvaliable}">
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using TINK.Model.Device;
|
using TINK.Model.Device;
|
||||||
|
using TINK.View.MasterDetail;
|
||||||
using TINK.ViewModel.Info;
|
using TINK.ViewModel.Info;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Xaml;
|
using Xamarin.Forms.Xaml;
|
||||||
|
@ -8,7 +9,7 @@ using Xamarin.Forms.Xaml;
|
||||||
namespace TINK.View.Contact
|
namespace TINK.View.Contact
|
||||||
{
|
{
|
||||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||||
public partial class ContactPage : ContentPage, IViewService
|
public partial class ContactPage : ContentPage, IViewService, IDetailPage
|
||||||
|
|
||||||
{
|
{
|
||||||
public ContactPage ()
|
public ContactPage ()
|
||||||
|
@ -46,8 +47,8 @@ namespace TINK.View.Contact
|
||||||
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
|
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
|
||||||
|
|
||||||
#if USEMASTERDETAIL || USEFLYOUT
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
public void ShowPage(ViewTypes p_oType, string title = null)
|
||||||
=> throw new NotImplementedException();
|
=> NavigationMasterDetail.ShowPage(p_oType.GetViewType(), title);
|
||||||
#else
|
#else
|
||||||
/// <summary> Shows a page.</summary>
|
/// <summary> Shows a page.</summary>
|
||||||
/// <param name="route">Route of the page to show.</param>
|
/// <param name="route">Route of the page to show.</param>
|
||||||
|
@ -68,15 +69,34 @@ namespace TINK.View.Contact
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Pushes a page onto the stack. </summary>
|
/// <summary> Pushes a page onto the stack. </summary>
|
||||||
/// <param name="p_oTypeOfPage">Page to display.</param>
|
/// <param name="typeOfPage">Page to display.</param>
|
||||||
public Task PushAsync(ViewTypes p_oTypeOfPage)
|
public async Task PushAsync(ViewTypes typeOfPage)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
|
||||||
|
if (!(Activator.CreateInstance(typeOfPage.GetViewType()) is IDetailPage detailPage))
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set reference to navigation object to be able to show page on newly shown detailPage.
|
||||||
|
detailPage.NavigationMasterDetail = NavigationMasterDetail;
|
||||||
|
|
||||||
|
await Navigation.PushAsync((Page)detailPage);
|
||||||
|
}
|
||||||
|
|
||||||
#if USCSHARP9
|
#if USCSHARP9
|
||||||
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
||||||
#else
|
#else
|
||||||
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate to perform navigation.
|
||||||
|
/// </summary>
|
||||||
|
public INavigationMasterDetail NavigationMasterDetail { set; private get; }
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
49
TINK/TINK/View/Contact/SelectStationPage.xaml
Normal file
49
TINK/TINK/View/Contact/SelectStationPage.xaml
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
xmlns:maps="clr-namespace:Xamarin.Forms.GoogleMaps;assembly=Xamarin.Forms.GoogleMaps"
|
||||||
|
xmlns:bindings="clr-namespace:Xamarin.Forms.GoogleMaps.Bindings;assembly=Xamarin.Forms.GoogleMaps.Bindings"
|
||||||
|
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
|
||||||
|
x:Class="TINK.View.Contact.SelectStationPage"
|
||||||
|
Title="{x:Static resources:AppResources.MarkingSelectStationPage}">
|
||||||
|
<ContentPage.Content>
|
||||||
|
<StackLayout>
|
||||||
|
<Grid
|
||||||
|
IsEnabled="{Binding IsMapPageEnabled}"
|
||||||
|
VerticalOptions="FillAndExpand">
|
||||||
|
<maps:Map WidthRequest="320" HeightRequest="800"
|
||||||
|
x:Name="MyMap"
|
||||||
|
IsShowingUser="False"
|
||||||
|
MapType="Street">
|
||||||
|
<maps:Map.Behaviors>
|
||||||
|
<bindings:BindingPinsBehavior Value="{Binding Pins}"/>
|
||||||
|
<bindings:PinClickedToCommandBehavior Command="{Binding PinClickedCommand}"/>
|
||||||
|
</maps:Map.Behaviors>
|
||||||
|
</maps:Map>
|
||||||
|
</Grid>
|
||||||
|
<StackLayout
|
||||||
|
Margin="6,3,6,6"
|
||||||
|
VerticalOptions="EndAndExpand"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Label
|
||||||
|
HeightRequest="20"
|
||||||
|
Text="{Binding StatusInfoText}"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
HorizontalOptions="FillAndExpand"/>
|
||||||
|
<ActivityIndicator IsRunning="{Binding IsRunning}"
|
||||||
|
IsVisible="{Binding IsRunning}"
|
||||||
|
HeightRequest="20"
|
||||||
|
VerticalOptions="CenterAndExpand"
|
||||||
|
HorizontalOptions="End">
|
||||||
|
<ActivityIndicator.WidthRequest>
|
||||||
|
<OnPlatform x:TypeArguments="x:Double" iOS="40" Android="40" WinPhone="40" />
|
||||||
|
</ActivityIndicator.WidthRequest>
|
||||||
|
<ActivityIndicator.Color>
|
||||||
|
<OnPlatform x:TypeArguments="Color"
|
||||||
|
iOS="#2499CE" WinPhone="#2499CE" />
|
||||||
|
</ActivityIndicator.Color>
|
||||||
|
</ActivityIndicator>
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
</ContentPage.Content>
|
||||||
|
</ContentPage>
|
209
TINK/TINK/View/Contact/SelectStationPage.xaml.cs
Normal file
209
TINK/TINK/View/Contact/SelectStationPage.xaml.cs
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
using TINK.View.MasterDetail;
|
||||||
|
#endif
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Xaml;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Xaml;
|
||||||
|
|
||||||
|
namespace TINK.View.Contact
|
||||||
|
{
|
||||||
|
using Serilog;
|
||||||
|
using TINK.ViewModel.Contact;
|
||||||
|
|
||||||
|
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
public partial class SelectStationPage : ContentPage, IViewService, IDetailPage
|
||||||
|
#else
|
||||||
|
public partial class MapPage : ContentPage, IViewService
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/// <summary> View model to notify about whether page appears or hides. </summary>
|
||||||
|
private SelectStationPageViewModel SelectStationPageViewModel { get; set; }
|
||||||
|
|
||||||
|
public SelectStationPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays alert message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">Title of message.</param>
|
||||||
|
/// <param name="message">Message to display.</param>
|
||||||
|
/// <param name="cancel">Type of buttons.</param>
|
||||||
|
public new async Task DisplayAlert(string title, string message, string cancel)
|
||||||
|
=> await App.Current.MainPage.DisplayAlert(title, message, cancel);
|
||||||
|
|
||||||
|
/// <summary> Displays alert message.</summary>
|
||||||
|
/// <param name="title">Title of message.</param>
|
||||||
|
/// <param name="message">Message to display.</param>
|
||||||
|
/// <param name="details">Detailed error description.</param>
|
||||||
|
/// <param name="cancel">Type of buttons.</param>
|
||||||
|
public async Task DisplayAdvancedAlert(
|
||||||
|
string title,
|
||||||
|
string message,
|
||||||
|
string details,
|
||||||
|
string cancel)
|
||||||
|
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
|
||||||
|
|
||||||
|
/// <summary> Displays detailed alert message.</summary>
|
||||||
|
/// <param name="title">Title of message.</param>
|
||||||
|
/// <param name="message">Message to display.</param>
|
||||||
|
/// <param name="details">Detailed error description.</param>
|
||||||
|
/// <param name="accept">Text of accept button.</param>
|
||||||
|
/// <param name="cancel">Text of cancel button.</param>
|
||||||
|
/// <returns>True if user pressed accept.</returns>
|
||||||
|
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
|
||||||
|
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays alert message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">Title of message.</param>
|
||||||
|
/// <param name="message">Message to display.</param>
|
||||||
|
/// <param name="accept">Text of accept button.</param>
|
||||||
|
/// <param name="cancel">Text of button.</param>
|
||||||
|
/// <returns>True if user pressed accept.</returns>
|
||||||
|
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
|
||||||
|
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and a page an shows it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type of page to show.</param>
|
||||||
|
public void ShowPage(ViewTypes type, string title = null)
|
||||||
|
=> NavigationMasterDetail.ShowPage(type.GetViewType(), title);
|
||||||
|
#else
|
||||||
|
/// <summary> Shows a page.</summary>
|
||||||
|
/// <param name="route">Route of the page to show.</param>
|
||||||
|
public async Task ShowPage(string route) => await Shell.Current.GoToAsync(route);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary> Pushes a page onto the modal stack. </summary>
|
||||||
|
/// <param name="typeOfPage">Type of page to display.</param>
|
||||||
|
public async Task PushModalAsync(ViewTypes typeOfPage)
|
||||||
|
=> await Navigation.PushModalAsync((Page)Activator.CreateInstance(typeOfPage.GetViewType()));
|
||||||
|
|
||||||
|
/// <summary> Pops a page from the modal stack. </summary>
|
||||||
|
public async Task PopModalAsync()
|
||||||
|
=> await Navigation.PopModalAsync();
|
||||||
|
|
||||||
|
/// <summary> Pushes a page onto the stack. </summary>
|
||||||
|
/// <param name="typeOfPage">Page to display.</param>
|
||||||
|
public async Task PushAsync(ViewTypes typeOfPage)
|
||||||
|
{
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
var page = Activator.CreateInstance(typeOfPage.GetViewType()) as IDetailPage;
|
||||||
|
#else
|
||||||
|
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
|
||||||
|
#endif
|
||||||
|
if (page == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
page.NavigationMasterDetail = NavigationMasterDetail;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
await Navigation.PushAsync((Page)page);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USCSHARP9
|
||||||
|
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
||||||
|
#else
|
||||||
|
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
/// <summary> Delegate to perform navigation.</summary>
|
||||||
|
public INavigationMasterDetail NavigationMasterDetail { private get; set; }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when page is shown.
|
||||||
|
/// Starts update process.
|
||||||
|
/// </summary>
|
||||||
|
protected async override void OnAppearing()
|
||||||
|
{
|
||||||
|
// Pass reference to member Navigation to show bikes at station x dialog.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Verbose("Constructing select station view model.");
|
||||||
|
|
||||||
|
#if TRYNOTBACKSTYLE
|
||||||
|
SelectStationPageViewModel = new SelectStationPageViewModel();
|
||||||
|
#else
|
||||||
|
SelectStationPageViewModel = new SelectStationPageViewModel(
|
||||||
|
App.ModelRoot,
|
||||||
|
App.PermissionsService,
|
||||||
|
App.BluetoothService,
|
||||||
|
App.GeolocationServicesContainer.Active,
|
||||||
|
(mapspan) => MyMap.MoveToRegion(mapspan),
|
||||||
|
this,
|
||||||
|
Navigation);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Constructing select station view model failed. {Exception}", exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BindingContext = SelectStationPageViewModel;
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
SelectStationPageViewModel.NavigationMasterDetail = NavigationMasterDetail;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Setting binding/ navigaton on select station failed. {Exception}", exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
base.OnAppearing();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
// Continue because styling is not essential.
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Pre move and scanle maps to avoid initial display of map in Rome.
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Verbose("Moving and scaling map.");
|
||||||
|
SelectStationPageViewModel.MoveAndScale(
|
||||||
|
(mapSpan) => MyMap.MoveToRegion(mapSpan),
|
||||||
|
App.ModelRoot.Uris.ActiveUri);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
// Continue because a map not beeing moved/ scaled is no reason for aborting startup.
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Moving and scaling map failed. {Exception}", exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Verbose("Invoking OnAppearing on select station view model.");
|
||||||
|
await SelectStationPageViewModel.OnAppearing();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Invoking OnAppearing on select station view model failed. {Exception}", exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using Plugin.Connectivity;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
#if USEMASTERDETAIL || USEFLYOUT
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
using TINK.View.MasterDetail;
|
using TINK.View.MasterDetail;
|
||||||
|
@ -33,11 +32,11 @@ namespace TINK.View.Map
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Displays alert message.
|
/// Displays alert message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="p_strTitle">Title of message.</param>
|
/// <param name="title">Title of message.</param>
|
||||||
/// <param name="p_strMessage">Message to display.</param>
|
/// <param name="message">Message to display.</param>
|
||||||
/// <param name="p_strCancel">Type of buttons.</param>
|
/// <param name="cancel">Type of buttons.</param>
|
||||||
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
|
public new async Task DisplayAlert(string title, string message, string cancel)
|
||||||
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
|
=> await App.Current.MainPage.DisplayAlert(title, message, cancel);
|
||||||
|
|
||||||
/// <summary> Displays alert message.</summary>
|
/// <summary> Displays alert message.</summary>
|
||||||
/// <param name="title">Title of message.</param>
|
/// <param name="title">Title of message.</param>
|
||||||
|
@ -64,23 +63,21 @@ namespace TINK.View.Map
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Displays alert message.
|
/// Displays alert message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="p_strTitle">Title of message.</param>
|
/// <param name="title">Title of message.</param>
|
||||||
/// <param name="p_strMessage">Message to display.</param>
|
/// <param name="message">Message to display.</param>
|
||||||
/// <param name="p_strAccept">Text of accept button.</param>
|
/// <param name="accept">Text of accept button.</param>
|
||||||
/// <param name="p_strCancel">Text of button.</param>
|
/// <param name="cancel">Text of button.</param>
|
||||||
/// <returns>True if user pressed accept.</returns>
|
/// <returns>True if user pressed accept.</returns>
|
||||||
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
|
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
|
||||||
{
|
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
|
||||||
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USEMASTERDETAIL || USEFLYOUT
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and a page an shows it.
|
/// Creates and a page an shows it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="p_oTypeOfPage">Type of page to show.</param>
|
/// <param name="p_oTypeOfPage">Type of page to show.</param>
|
||||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
public void ShowPage(ViewTypes type, string title = null)
|
||||||
=> NavigationMasterDetail.ShowPage(p_oType.GetViewType(), p_strTitle);
|
=> NavigationMasterDetail.ShowPage(type.GetViewType(), title);
|
||||||
#else
|
#else
|
||||||
/// <summary> Shows a page.</summary>
|
/// <summary> Shows a page.</summary>
|
||||||
/// <param name="route">Route of the page to show.</param>
|
/// <param name="route">Route of the page to show.</param>
|
||||||
|
@ -88,24 +85,20 @@ namespace TINK.View.Map
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// <summary> Pushes a page onto the modal stack. </summary>
|
/// <summary> Pushes a page onto the modal stack. </summary>
|
||||||
/// <param name="p_oTypeOfPage">Type of page to display.</param>
|
/// <param name="typeOfPage">Type of page to display.</param>
|
||||||
public async Task PushModalAsync(ViewTypes p_oTypeOfPage)
|
public async Task PushModalAsync(ViewTypes typeOfPage)
|
||||||
{
|
=> await Navigation.PushModalAsync((Page)Activator.CreateInstance(typeOfPage.GetViewType()));
|
||||||
await Navigation.PushModalAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Pops a page from the modal stack. </summary>
|
/// <summary> Pops a page from the modal stack. </summary>
|
||||||
public async Task PopModalAsync()
|
public async Task PopModalAsync()
|
||||||
{
|
=> await Navigation.PopModalAsync();
|
||||||
await Navigation.PopModalAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Pushes a page onto the stack. </summary>
|
/// <summary> Pushes a page onto the stack. </summary>
|
||||||
/// <param name="p_oTypeOfPage">Page to display.</param>
|
/// <param name="typeOfPage">Page to display.</param>
|
||||||
public async Task PushAsync(ViewTypes p_oTypeOfPage)
|
public async Task PushAsync(ViewTypes typeOfPage)
|
||||||
{
|
{
|
||||||
#if USEMASTERDETAIL || USEFLYOUT
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType()) as IDetailPage;
|
var page = Activator.CreateInstance(typeOfPage.GetViewType()) as IDetailPage;
|
||||||
#else
|
#else
|
||||||
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
|
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
|
||||||
#endif
|
#endif
|
||||||
|
@ -144,7 +137,7 @@ namespace TINK.View.Map
|
||||||
Log.ForContext<MainPage>().Verbose("Constructing map page view model.");
|
Log.ForContext<MainPage>().Verbose("Constructing map page view model.");
|
||||||
|
|
||||||
#if TRYNOTBACKSTYLE
|
#if TRYNOTBACKSTYLE
|
||||||
m_oMapPageViewModel = new MapPageViewModel();
|
MapPageViewModel = new MapPageViewModel();
|
||||||
#else
|
#else
|
||||||
MapPageViewModel = new MapPageViewModel(
|
MapPageViewModel = new MapPageViewModel(
|
||||||
App.ModelRoot,
|
App.ModelRoot,
|
||||||
|
@ -194,7 +187,6 @@ namespace TINK.View.Map
|
||||||
Log.ForContext<MainPage>().Error("IOS specific styling of map page failed. {Exception}", exception);
|
Log.ForContext<MainPage>().Error("IOS specific styling of map page failed. {Exception}", exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
@ -214,7 +206,6 @@ namespace TINK.View.Map
|
||||||
(mapSpan) => MyMap.MoveToRegion(mapSpan),
|
(mapSpan) => MyMap.MoveToRegion(mapSpan),
|
||||||
App.ModelRoot.Uris.ActiveUri,
|
App.ModelRoot.Uris.ActiveUri,
|
||||||
App.ModelRoot.GroupFilterMapPage);
|
App.ModelRoot.GroupFilterMapPage);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(Exception exception)
|
catch(Exception exception)
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,6 +62,9 @@ namespace TINK.View
|
||||||
case ViewTypes.ContactPage:
|
case ViewTypes.ContactPage:
|
||||||
return typeof(ContactPage);
|
return typeof(ContactPage);
|
||||||
|
|
||||||
|
case ViewTypes.SelectStationPage:
|
||||||
|
return typeof(SelectStationPage);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return typeof(ContentPage);
|
return typeof(ContentPage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,29 +47,21 @@ namespace TINK.View.WhatsNew.Agb
|
||||||
|
|
||||||
/// <summary> Invoked when page is shown. </summary>
|
/// <summary> Invoked when page is shown. </summary>
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
=> await agbViewModel.OnAppearing();
|
||||||
await agbViewModel.OnAppearing();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Reference to view model.</summary>
|
/// <summary> Reference to view model.</summary>
|
||||||
AgbViewModel agbViewModel;
|
AgbViewModel agbViewModel;
|
||||||
|
|
||||||
public async Task PopModalAsync()
|
public async Task PopModalAsync()
|
||||||
{
|
=> await Navigation.PopModalAsync();
|
||||||
await Navigation.PopModalAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Pushes a page onto the stack. </summary>
|
/// <summary> Pushes a page onto the stack. </summary>
|
||||||
/// <param name="p_oTypeOfPage">Page to display.</param>
|
/// <param name="p_oTypeOfPage">Page to display.</param>
|
||||||
public Task PushAsync(ViewTypes p_oTypeOfPage)
|
public Task PushAsync(ViewTypes p_oTypeOfPage)
|
||||||
{
|
=> throw new NotImplementedException();
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
|
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
|
||||||
{
|
=> throw new NotImplementedException();
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USEMASTERDETAIL || USEFLYOUT
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
|
||||||
|
|
|
@ -417,6 +417,10 @@ namespace TINK.Model
|
||||||
{
|
{
|
||||||
new Version(3, 0, 241),
|
new Version(3, 0, 241),
|
||||||
AppResources.ChangeLog3_0_241
|
AppResources.ChangeLog3_0_241
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new Version(3, 0, 242),
|
||||||
|
AppResources.ChangeLog3_0_242
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,15 @@ namespace TINK.MultilingualResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Select station.
|
||||||
|
/// </summary>
|
||||||
|
public static string ActionSelectStation {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ActionSelectStation", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Loading bikes located at station....
|
/// Looks up a localized string similar to Loading bikes located at station....
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -751,6 +760,15 @@ namespace TINK.MultilingualResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Select station page added to ease getting operator specific contact information..
|
||||||
|
/// </summary>
|
||||||
|
public static string ChangeLog3_0_242 {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ChangeLog3_0_242", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Lock of rented bike can not be found..
|
/// Looks up a localized string similar to Lock of rented bike can not be found..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1066,7 +1084,7 @@ namespace TINK.MultilingualResources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Contact {0}.
|
/// Looks up a localized string similar to Contact.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MarkingContactPageTitle {
|
public static string MarkingContactPageTitle {
|
||||||
get {
|
get {
|
||||||
|
@ -1237,6 +1255,15 @@ namespace TINK.MultilingualResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Select Station.
|
||||||
|
/// </summary>
|
||||||
|
public static string MarkingSelectStationPage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("MarkingSelectStationPage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Settings.
|
/// Looks up a localized string similar to Settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -622,10 +622,19 @@ Layout Anzeige Radnamen und nummern verbessert.</value>
|
||||||
<data name="MarkingContactNoStationInfoAvailableNoButton" xml:space="preserve">
|
<data name="MarkingContactNoStationInfoAvailableNoButton" xml:space="preserve">
|
||||||
<value>Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</value>
|
<value>Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MarkingContactPageTitle" xml:space="preserve">
|
|
||||||
<value>Kontakt {0}</value>
|
|
||||||
</data>
|
|
||||||
<data name="ChangeLog3_0_241" xml:space="preserve">
|
<data name="ChangeLog3_0_241" xml:space="preserve">
|
||||||
<value>Auf der Kontaktseite werden Kontaktinformationen betreiberspezifisch angezeigt.</value>
|
<value>Auf der Kontaktseite werden Kontaktinformationen betreiberspezifisch angezeigt.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="MarkingSelectStationPage" xml:space="preserve">
|
||||||
|
<value>Station Auswählen</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionSelectStation" xml:space="preserve">
|
||||||
|
<value>Station auswählen</value>
|
||||||
|
</data>
|
||||||
|
<data name="MarkingContactPageTitle" xml:space="preserve">
|
||||||
|
<value>Kontakt</value>
|
||||||
|
</data>
|
||||||
|
<data name="ChangeLog3_0_242" xml:space="preserve">
|
||||||
|
<value>Seite zur Auswahl einer Station hinzugefügt zum Abrufen von Kontaktinformationen.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -719,9 +719,18 @@ Layout of bike names and id display improved.</value>
|
||||||
<value>Please login to reserve bikes! Tap <font color="blue"><u>here</u></font> to switch to login page.</value>
|
<value>Please login to reserve bikes! Tap <font color="blue"><u>here</u></font> to switch to login page.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MarkingContactPageTitle" xml:space="preserve">
|
<data name="MarkingContactPageTitle" xml:space="preserve">
|
||||||
<value>Contact {0}</value>
|
<value>Contact</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ChangeLog3_0_241" xml:space="preserve">
|
<data name="ChangeLog3_0_241" xml:space="preserve">
|
||||||
<value>Bike sharing system operator specific support info displayed on contact page.</value>
|
<value>Bike sharing system operator specific support info displayed on contact page.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ActionSelectStation" xml:space="preserve">
|
||||||
|
<value>Select station</value>
|
||||||
|
</data>
|
||||||
|
<data name="MarkingSelectStationPage" xml:space="preserve">
|
||||||
|
<value>Select Station</value>
|
||||||
|
</data>
|
||||||
|
<data name="ChangeLog3_0_242" xml:space="preserve">
|
||||||
|
<value>Select station page added to ease getting operator specific contact information.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -833,14 +833,26 @@ Layout Anzeige Radnamen und nummern verbessert.</target>
|
||||||
<source>Please open a bike station page to to contact the bike sharing operator.</source>
|
<source>Please open a bike station page to to contact the bike sharing operator.</source>
|
||||||
<target state="translated">Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</target>
|
<target state="translated">Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="MarkingContactPageTitle" translate="yes" xml:space="preserve">
|
|
||||||
<source>Contact {0}</source>
|
|
||||||
<target state="translated">Kontakt {0}</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="ChangeLog3_0_241" translate="yes" xml:space="preserve">
|
<trans-unit id="ChangeLog3_0_241" translate="yes" xml:space="preserve">
|
||||||
<source>Bike sharing system operator specific support info displayed on contact page.</source>
|
<source>Bike sharing system operator specific support info displayed on contact page.</source>
|
||||||
<target state="translated">Auf der Kontaktseite werden Kontaktinformationen betreiberspezifisch angezeigt.</target>
|
<target state="translated">Auf der Kontaktseite werden Kontaktinformationen betreiberspezifisch angezeigt.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="MarkingSelectStationPage" translate="yes" xml:space="preserve">
|
||||||
|
<source>Select Station</source>
|
||||||
|
<target state="translated">Station Auswählen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="ActionSelectStation" translate="yes" xml:space="preserve">
|
||||||
|
<source>Select station</source>
|
||||||
|
<target state="translated">Station auswählen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="MarkingContactPageTitle" translate="yes" xml:space="preserve">
|
||||||
|
<source>Contact</source>
|
||||||
|
<target state="translated">Kontakt</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="ChangeLog3_0_242" translate="yes" xml:space="preserve">
|
||||||
|
<source>Select station page added to ease getting operator specific contact information.</source>
|
||||||
|
<target state="translated">Seite zur Auswahl einer Station hinzugefügt zum Abrufen von Kontaktinformationen.</target>
|
||||||
|
</trans-unit>
|
||||||
</group>
|
</group>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
||||||
}
|
}
|
||||||
catch (Exception p_oException)
|
catch (Exception p_oException)
|
||||||
{
|
{
|
||||||
Log.ForContext<BikesViewModel>().Error("Ein unerwarteter Fehler ist auf der Seite Anmelden aufgetreten. Kontext: Aufruf nach Reservierungsversuch ohne Anmeldung. {@Exception}", p_oException);
|
Log.ForContext<BikesViewModel>().Error("Ein unerwarteter Fehler ist in der Klasse NotLoggedIn aufgetreten. Kontext: Aufruf nach Reservierungsversuch ohne Anmeldung. {@Exception}", p_oException);
|
||||||
IsIdle = true;
|
IsIdle = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||||
}
|
}
|
||||||
catch (Exception p_oException)
|
catch (Exception p_oException)
|
||||||
{
|
{
|
||||||
Log.ForContext<BikesViewModel>().Error("Ein unerwarteter Fehler ist auf der Seite Anmelden aufgetreten. Kontext: Aufruf nach Reservierungsversuch ohne Anmeldung. {@Exception}", p_oException);
|
Log.ForContext<BikesViewModel>().Error("Ein unerwarteter Fehler ist in der Klasse NotLoggedIn aufgetreten. Kontext: Aufruf nach Reservierungsversuch ohne Anmeldung. {@Exception}", p_oException);
|
||||||
BikesViewModel.IsIdle = true;
|
BikesViewModel.IsIdle = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,16 +178,16 @@ namespace TINK.ViewModel.BikesAtStation
|
||||||
}
|
}
|
||||||
catch (Exception p_oException)
|
catch (Exception p_oException)
|
||||||
{
|
{
|
||||||
Log.Error("Ein unerwarteter Fehler ist auf der Seite Anmelden aufgetreten. Kontext: Klick auf Hinweistext auf Station N- seite ohne Anmeldung. {@Exception}", p_oException);
|
Log.Error("Ein unerwarteter Fehler ist in der Klasse BikesAtStationPageViewModel aufgetreten. Kontext: Klick auf Hinweistext auf Station N- seite ohne Anmeldung. {@Exception}", p_oException);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Opens login page. </summary>
|
/// <summary> Opens support. </summary>
|
||||||
#if USEMASTERDETAIL || USEFLYOUT
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
public void OpenSupportPageAsync()
|
public void OpenSupportPageAsync()
|
||||||
#else
|
#else
|
||||||
public async Task OpenLoginPageAsync()
|
public async Task OpenSupportPageAsync()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -163,7 +163,41 @@ namespace TINK.ViewModel.Info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary> Command object to bind login button to view model. </summary>
|
/// <summary> Command object to bind login button to view model. </summary>
|
||||||
|
public ICommand OnSelectStationRequest
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
=> new Xamarin.Forms.Command(() => OpenSelectStationPage());
|
||||||
|
#else
|
||||||
|
=> new Xamarin.Forms.Command(async () => await OpenSelectStationPageAsync());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary> Opens login page. </summary>
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
public void OpenSelectStationPage()
|
||||||
|
#else
|
||||||
|
public async Task OpenSelectStationPageAsync()
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Switch to map page
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
ViewService.PushAsync(ViewTypes.SelectStationPage);
|
||||||
|
#else
|
||||||
|
await ViewService.ShowPage("//SelectStationPage");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (Exception p_oException)
|
||||||
|
{
|
||||||
|
Log.Error("Ein unerwarteter Fehler ist in der Klasse ContactPageViewModel aufgetreten. Kontext: Klick auf Hinweistext auf Station N- seite ohne Anmeldung. {@Exception}", p_oException);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> Command object to bind phone call button. </summary>
|
||||||
public ICommand OnPhoneRequest
|
public ICommand OnPhoneRequest
|
||||||
=> new Command(
|
=> new Command(
|
||||||
async () => await DoPhoneCall(),
|
async () => await DoPhoneCall(),
|
||||||
|
|
647
TINKLib/ViewModel/Contact/SelectStationPageViewModel.cs
Normal file
647
TINKLib/ViewModel/Contact/SelectStationPageViewModel.cs
Normal file
|
@ -0,0 +1,647 @@
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using TINK.View;
|
||||||
|
using TINK.Model.Station;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using TINK.Model.Bike;
|
||||||
|
using TINK.Repository.Exception;
|
||||||
|
using TINK.Model;
|
||||||
|
using Serilog;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
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;
|
||||||
|
using Plugin.Permissions;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
using System.Threading;
|
||||||
|
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;
|
||||||
|
|
||||||
|
namespace TINK.ViewModel.Contact
|
||||||
|
{
|
||||||
|
public class SelectStationPageViewModel : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary> Holds the count of custom icons availalbe.</summary>
|
||||||
|
private const int CUSTOM_ICONS_COUNT = 30;
|
||||||
|
|
||||||
|
/// <summary> Reference on view servcie to show modal notifications and to perform navigation. </summary>
|
||||||
|
private IViewService ViewService { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the exception which occurred getting bikes occupied information.
|
||||||
|
/// </summary>
|
||||||
|
private Exception m_oException;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service to query/ manage permissions (location) of the app.
|
||||||
|
/// </summary>
|
||||||
|
private IPermissions PermissionsService { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service to manage bluetooth stack.
|
||||||
|
/// </summary>
|
||||||
|
private Plugin.BLE.Abstractions.Contracts.IBluetoothLE BluetoothService { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> Notifies view about changes. </summary>
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> Reference on the tink app instance. </summary>
|
||||||
|
private ITinkApp TinkApp { get; }
|
||||||
|
|
||||||
|
/// <summary>Delegate to perform navigation.</summary>
|
||||||
|
private INavigation m_oNavigation;
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
/// <summary>Delegate to perform navigation.</summary>
|
||||||
|
private INavigationMasterDetail m_oNavigationMasterDetail;
|
||||||
|
#endif
|
||||||
|
private ObservableCollection<Pin> pins;
|
||||||
|
|
||||||
|
public ObservableCollection<Pin> Pins
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (pins == null)
|
||||||
|
pins = new ObservableCollection<Pin>(); // If view model is not binding context pins collection must be set programmatically.
|
||||||
|
|
||||||
|
return pins;
|
||||||
|
}
|
||||||
|
|
||||||
|
set => pins = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Delegate to move map to region.</summary>
|
||||||
|
private Action<MapSpan> m_oMoveToRegionDelegate;
|
||||||
|
|
||||||
|
/// <summary> False if user tabed on station marker to show bikes at a given station.</summary>
|
||||||
|
private bool isMapPageEnabled = false;
|
||||||
|
|
||||||
|
Model.Services.Geolocation.IGeolocation GeolocationService { get; }
|
||||||
|
|
||||||
|
/// <summary> False if user tabed on station marker to show bikes at a given station.</summary>
|
||||||
|
public bool IsMapPageEnabled
|
||||||
|
{
|
||||||
|
get => isMapPageEnabled;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (isMapPageEnabled == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isMapPageEnabled = value;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsMapPageEnabled)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Prevents an invalid instane to be created. </summary>
|
||||||
|
/// <param name="tinkApp"> Reference to tink app model.</param>
|
||||||
|
/// <param name="moveToRegionDelegate">Delegate to center map and set zoom level.</param>
|
||||||
|
/// <param name="viewService">View service to notify user.</param>
|
||||||
|
/// <param name="navigation">Interface to navigate.</param>
|
||||||
|
public SelectStationPageViewModel(
|
||||||
|
ITinkApp tinkApp,
|
||||||
|
IPermissions permissionsService,
|
||||||
|
Plugin.BLE.Abstractions.Contracts.IBluetoothLE bluetoothService,
|
||||||
|
IGeolocation geolocationService,
|
||||||
|
Action<MapSpan> moveToRegionDelegate,
|
||||||
|
IViewService viewService,
|
||||||
|
INavigation navigation)
|
||||||
|
{
|
||||||
|
TinkApp = tinkApp
|
||||||
|
?? throw new ArgumentException("Can not instantiate map page view model- object. No tink app object available.");
|
||||||
|
|
||||||
|
PermissionsService = permissionsService ??
|
||||||
|
throw new ArgumentException($"Can not instantiate {nameof(SelectStationPageViewModel)}. Permissions service object must never be null.");
|
||||||
|
|
||||||
|
BluetoothService = bluetoothService ??
|
||||||
|
throw new ArgumentException($"Can not instantiate {nameof(SelectStationPageViewModel)}. Bluetooth service object must never be null.");
|
||||||
|
|
||||||
|
GeolocationService = geolocationService ??
|
||||||
|
throw new ArgumentException($"Can not instantiate {nameof(SelectStationPageViewModel)}. 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.");
|
||||||
|
|
||||||
|
ViewService = viewService
|
||||||
|
?? throw new ArgumentException("Can not instantiate map page view model- object. No view available.");
|
||||||
|
|
||||||
|
m_oNavigation = navigation
|
||||||
|
?? throw new ArgumentException("Can not instantiate map page view model- object. No navigation service available.");
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
m_oNavigationMasterDetail = new EmptyNavigationMasterDetail();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IsConnected = TinkApp.GetIsConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USEMASTERDETAIL || USEFLYOUT
|
||||||
|
/// <summary> Delegate to perform navigation.</summary>
|
||||||
|
public INavigationMasterDetail NavigationMasterDetail
|
||||||
|
{
|
||||||
|
set { m_oNavigationMasterDetail = value; }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public Command<PinClickedEventArgs> PinClickedCommand => new Command<PinClickedEventArgs>(
|
||||||
|
args =>
|
||||||
|
{
|
||||||
|
OnStationClicked(args.Pin.Tag.ToString());
|
||||||
|
args.Handled = true; // Prevents map to be centered to selected pin.
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// One time setup: Sets pins into map and connects to events.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializePins(StationDictionary stations)
|
||||||
|
{
|
||||||
|
// Add pins to stations.
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Debug($"Request to draw {stations.Count} pins.");
|
||||||
|
foreach (var station in stations)
|
||||||
|
{
|
||||||
|
if (station.Position == null)
|
||||||
|
{
|
||||||
|
// There should be no reason for a position object to be null but this alreay occurred in past.
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Postion object of station {@l_oStation} is null.", station);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var l_oPin = new Pin
|
||||||
|
{
|
||||||
|
|
||||||
|
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 = station.Id,
|
||||||
|
IsVisible = false, // Set to false to prevent showing default icons (flickering).
|
||||||
|
};
|
||||||
|
|
||||||
|
Pins.Add(l_oPin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Update all stations from TINK. </summary>
|
||||||
|
/// <param name="stationsColorList">List of colors to apply.</param>
|
||||||
|
private void UpdatePinsColor(IList<Color> stationsColorList)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Debug($"Starting update of stations pins color for {stationsColorList.Count} stations...");
|
||||||
|
|
||||||
|
// Update colors of pins.
|
||||||
|
for (int pinIndex = 0; pinIndex < stationsColorList.Count; pinIndex++)
|
||||||
|
{
|
||||||
|
|
||||||
|
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(stationsColorList[pinIndex]);
|
||||||
|
|
||||||
|
var l_iName = $"{indexPartPrefix.ToString().PadLeft(2, '0')}_{colorPartPrefix}{(DeviceInfo.Platform == DevicePlatform.Android ? ".png" : string.Empty)}";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Pins[pinIndex].Icon = BitmapDescriptorFactory.FromBundle(l_iName);
|
||||||
|
}
|
||||||
|
catch (Exception l_oException)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Station icon {l_strName} can not be loaded. {@l_oException}.", l_oException);
|
||||||
|
Pins[pinIndex].Label = stationId.ToString();
|
||||||
|
Pins[pinIndex].Icon = BitmapDescriptorFactory.DefaultMarker(stationsColorList[pinIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pins[pinIndex].IsVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pinsCount = Pins.Count;
|
||||||
|
for (int pinIndex = stationsColorList.Count; pinIndex < pinsCount; pinIndex++)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error($"Unexpected count of pins detected. Expected {stationsColorList.Count} but is {pinsCount}.");
|
||||||
|
Pins[pinIndex].IsVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Debug("Update of stations pins color done.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Gets the color related part of the ressrouce name.</summary>
|
||||||
|
/// <param name="color">Color to get name for.</param>
|
||||||
|
/// <returns>Resource name.</returns>
|
||||||
|
private static string GetRessourceNameColorPart(Color color)
|
||||||
|
{
|
||||||
|
if (color == Color.Blue)
|
||||||
|
{
|
||||||
|
return "Blue";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == Color.Green)
|
||||||
|
{
|
||||||
|
return "Green";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == Color.LightBlue)
|
||||||
|
{
|
||||||
|
return "LightBlue";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == Color.Red)
|
||||||
|
{
|
||||||
|
return "Red";
|
||||||
|
}
|
||||||
|
|
||||||
|
return color.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when page is shown.
|
||||||
|
/// Starts update process.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="p_oFilterDictionaryMapPage">Holds map page filter settings.</param>
|
||||||
|
/// <param name="p_oPolling">Holds polling management object.</param>
|
||||||
|
/// <param name="p_bIsShowWhatsNewRequired">If true whats new page will be shown.</param>
|
||||||
|
public async Task OnAppearing()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsRunning = true;
|
||||||
|
// Process map page.
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Information(
|
||||||
|
$"Current UI language is {Thread.CurrentThread.CurrentUICulture.Name}.");
|
||||||
|
|
||||||
|
if (Pins.Count <= 0)
|
||||||
|
{
|
||||||
|
ActionText = AppResources.ActivityTextMyBikesLoadingBikes;
|
||||||
|
|
||||||
|
// Check location permission
|
||||||
|
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
|
||||||
|
if (TinkApp.CenterMapToCurrentLocation
|
||||||
|
&& !GeolocationService.IsSimulation
|
||||||
|
&& status != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
||||||
|
{
|
||||||
|
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
|
||||||
|
|
||||||
|
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
||||||
|
{
|
||||||
|
var dialogResult = await ViewService.DisplayAlert(
|
||||||
|
AppResources.MessageTitleHint,
|
||||||
|
AppResources.MessageCenterMapLocationPermissionOpenDialog,
|
||||||
|
AppResources.MessageAnswerYes,
|
||||||
|
AppResources.MessageAnswerNo);
|
||||||
|
|
||||||
|
if (dialogResult)
|
||||||
|
{
|
||||||
|
// User decided to give access to locations permissions.
|
||||||
|
PermissionsService.OpenAppSettings();
|
||||||
|
ActionText = "";
|
||||||
|
IsRunning = false;
|
||||||
|
IsMapPageEnabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move and scale before getting stations and bikes which takes some time.
|
||||||
|
ActionText = AppResources.ActivityTextCenterMap;
|
||||||
|
Location currentLocation = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentLocation = TinkApp.CenterMapToCurrentLocation
|
||||||
|
? await GeolocationService.GetAsync()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Getting location failed. {Exception}", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveAndScale(m_oMoveToRegionDelegate, TinkApp.Uris.ActiveUri, currentLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionText = AppResources.ActivityTextMapLoadingStationsAndBikes;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Map was not yet initialized.
|
||||||
|
// Get stations from Copri
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Verbose("No pins detected on page.");
|
||||||
|
if (resultStationsAndBikes.Response.StationsAll.CopriVersion < CopriCallsStatic.UnsupportedVersionLower)
|
||||||
|
{
|
||||||
|
await ViewService.DisplayAlert(
|
||||||
|
AppResources.MessageWaring,
|
||||||
|
string.Format(AppResources.MessageCopriVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)),
|
||||||
|
AppResources.MessageAnswerOk);
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error($"Outdated version of app detected. Version expected is {resultStationsAndBikes.Response.StationsAll.CopriVersion}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultStationsAndBikes.Response.StationsAll.CopriVersion >= CopriCallsStatic.UnsupportedVersionUpper)
|
||||||
|
{
|
||||||
|
await ViewService.DisplayAlert(
|
||||||
|
AppResources.MessageWaring,
|
||||||
|
string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)),
|
||||||
|
AppResources.MessageAnswerOk);
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error($"Outdated version of app detected. Version expected is {resultStationsAndBikes.Response.StationsAll.CopriVersion}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set pins to their positions on map.
|
||||||
|
InitializePins(resultStationsAndBikes.Response.StationsAll);
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Verbose("Update of pins done.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (resultStationsAndBikes.Exception?.GetType() == typeof(AuthcookieNotDefinedException))
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().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 ViewService.DisplayAlert(
|
||||||
|
AppResources.MessageWaring,
|
||||||
|
AppResources.MessageMapPageErrorAuthcookieUndefined,
|
||||||
|
AppResources.MessageAnswerOk);
|
||||||
|
|
||||||
|
await TinkApp.GetConnector(IsConnected).Command.DoLogout();
|
||||||
|
TinkApp.ActiveUser.Logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update pin colors.
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Verbose("Starting update pins color...");
|
||||||
|
|
||||||
|
var colors = GetStationColors(
|
||||||
|
Pins.Select(x => x.Tag.ToString()).ToList(),
|
||||||
|
resultStationsAndBikes.Response.Bikes);
|
||||||
|
|
||||||
|
// Update pins color form count of bikes located at station.
|
||||||
|
UpdatePinsColor(colors);
|
||||||
|
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Verbose("Update pins color done.");
|
||||||
|
|
||||||
|
Exception = resultStationsAndBikes.Exception;
|
||||||
|
ActionText = "";
|
||||||
|
IsRunning = false;
|
||||||
|
IsMapPageEnabled = true;
|
||||||
|
}
|
||||||
|
catch (Exception l_oException)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error($"An error occurred switching view TINK/ Konrad.\r\n{l_oException.Message}");
|
||||||
|
|
||||||
|
IsRunning = false;
|
||||||
|
|
||||||
|
await ViewService.DisplayAlert(
|
||||||
|
"Fehler",
|
||||||
|
$"Beim Anzeigen der Fahrradstandorte- Seite ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
|
||||||
|
"OK");
|
||||||
|
|
||||||
|
IsMapPageEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Moves map and scales visible region depending on active filter. </summary>
|
||||||
|
public static void MoveAndScale(
|
||||||
|
Action<MapSpan> moveToRegionDelegate,
|
||||||
|
Uri activeUri,
|
||||||
|
Location currentLocation = null)
|
||||||
|
{
|
||||||
|
if (currentLocation != null)
|
||||||
|
{
|
||||||
|
// Move to current location.
|
||||||
|
moveToRegionDelegate(MapSpan.FromCenterAndRadius(
|
||||||
|
new Xamarin.Forms.GoogleMaps.Position(currentLocation.Latitude, currentLocation.Longitude),
|
||||||
|
Distance.FromKilometers(1.0)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center map to Freiburg
|
||||||
|
moveToRegionDelegate(MapSpan.FromCenterAndRadius(
|
||||||
|
new Xamarin.Forms.GoogleMaps.Position(47.995865, 7.815086),
|
||||||
|
Distance.FromKilometers(2.9)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> User clicked on a bike. </summary>
|
||||||
|
/// <param name="selectedStationId">Id of station user clicked on.</param>
|
||||||
|
public async void OnStationClicked(string selectedStationId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Information($"User taped station {selectedStationId}.");
|
||||||
|
|
||||||
|
// Lock action to prevent multiple instances of "BikeAtStation" being opened.
|
||||||
|
IsMapPageEnabled = false;
|
||||||
|
|
||||||
|
TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId)
|
||||||
|
?? new Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updatd in background task.
|
||||||
|
|
||||||
|
#if TRYNOTBACKSTYLE
|
||||||
|
m_oNavigation.ShowPage(
|
||||||
|
typeof(BikesAtStationPage),
|
||||||
|
p_strStationName);
|
||||||
|
#else
|
||||||
|
// Show page.
|
||||||
|
ViewService.ShowPage(ViewTypes.ContactPage, AppResources.MarkingContactPageTitle);
|
||||||
|
|
||||||
|
IsMapPageEnabled = true;
|
||||||
|
ActionText = "";
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
IsMapPageEnabled = true;
|
||||||
|
ActionText = "";
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {Exception}", exception);
|
||||||
|
await ViewService.DisplayAlert(
|
||||||
|
"Fehler",
|
||||||
|
$"Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {exception.Message}",
|
||||||
|
"OK");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of station color for all stations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stationsId">Station id list to get color for.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static IList<Color> GetStationColors(
|
||||||
|
IEnumerable<string> stationsId,
|
||||||
|
BikeCollection bikesAll)
|
||||||
|
{
|
||||||
|
if (stationsId == null)
|
||||||
|
{
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Debug("No stations available to update color for.");
|
||||||
|
return new List<Color>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bikesAll == null)
|
||||||
|
{
|
||||||
|
// If object is null an error occurred querrying bikes availalbe or bikes occpied which results in an unknown state.
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Error("No bikes available to determine pins color.");
|
||||||
|
return new List<Color>(stationsId.Select(x => Color.Blue));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get state for each station.
|
||||||
|
var colors = new List<Color>();
|
||||||
|
foreach (var stationId in stationsId)
|
||||||
|
{
|
||||||
|
// Get color of given station.
|
||||||
|
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
|
||||||
|
colors.Add(Color.LightBlue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bikesAtStation.ToList().Count > 0)
|
||||||
|
{
|
||||||
|
// There is at least one bike available
|
||||||
|
colors.Add(Color.Green);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
colors.Add(Color.Red);
|
||||||
|
}
|
||||||
|
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exception which occurred getting bike information.
|
||||||
|
/// </summary>
|
||||||
|
public Exception Exception
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_oException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
var statusInfoText = StatusInfoText;
|
||||||
|
m_oException = value;
|
||||||
|
if (statusInfoText == StatusInfoText)
|
||||||
|
{
|
||||||
|
// Nothing to do because value did not change.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StatusInfoText)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Holds info about current action. </summary>
|
||||||
|
private string actionText;
|
||||||
|
|
||||||
|
/// <summary> Holds info about current action. </summary>
|
||||||
|
private string ActionText
|
||||||
|
{
|
||||||
|
get => actionText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var statusInfoText = StatusInfoText;
|
||||||
|
actionText = value;
|
||||||
|
if (statusInfoText == StatusInfoText)
|
||||||
|
{
|
||||||
|
// Nothing to do because value did not change.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StatusInfoText)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Used to block more than on copri requests at a given time.</summary>
|
||||||
|
private bool isRunning = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if any action can be performed (request and cancel request)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRunning
|
||||||
|
{
|
||||||
|
get => isRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == isRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Log.ForContext<SelectStationPageViewModel>().Debug($"Switch value of {nameof(isRunning)} to {value}.");
|
||||||
|
isRunning = value;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRunning)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> Holds information whether app is connected to web or not. </summary>
|
||||||
|
private bool? isConnected = null;
|
||||||
|
|
||||||
|
/// <summary>Exposes the is connected state. </summary>
|
||||||
|
private bool IsConnected
|
||||||
|
{
|
||||||
|
get => isConnected ?? false;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var statusInfoText = StatusInfoText;
|
||||||
|
isConnected = value;
|
||||||
|
if (statusInfoText == StatusInfoText)
|
||||||
|
{
|
||||||
|
// Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StatusInfoText)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Holds the status information text. </summary>
|
||||||
|
public string StatusInfoText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Exception != null)
|
||||||
|
{
|
||||||
|
// An error occurred getting data from copri.
|
||||||
|
return TinkApp.IsReportLevelVerbose
|
||||||
|
? Exception.GetShortErrorInfoText()
|
||||||
|
: AppResources.ActivityTextException;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsConnected)
|
||||||
|
{
|
||||||
|
return AppResources.ActivityTextConnectionStateOffline;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionText ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ using TINK.Model.Connector;
|
||||||
using TINK.Model.Services.CopriApi;
|
using TINK.Model.Services.CopriApi;
|
||||||
using Plugin.Permissions;
|
using Plugin.Permissions;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Plugin.BLE;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using TINK.MultilingualResources;
|
using TINK.MultilingualResources;
|
||||||
using TINK.Services.BluetoothLock;
|
using TINK.Services.BluetoothLock;
|
||||||
|
@ -37,11 +36,11 @@ namespace TINK.ViewModel.Map
|
||||||
{
|
{
|
||||||
public class MapPageViewModel : INotifyPropertyChanged
|
public class MapPageViewModel : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
/// <summary> Holds the count of custom idcons availalbe.</summary>
|
/// <summary> Holds the count of custom icons availalbe.</summary>
|
||||||
private const int CUSTOM_ICONS_COUNT = 30;
|
private const int CUSTOM_ICONS_COUNT = 30;
|
||||||
|
|
||||||
/// <summary> Reference on view servcie to show modal notifications and to perform navigation. </summary>
|
/// <summary> Reference on view servcie to show modal notifications and to perform navigation. </summary>
|
||||||
private IViewService m_oViewService;
|
private IViewService ViewService { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds the exception which occurred getting bikes occupied information.
|
/// Holds the exception which occurred getting bikes occupied information.
|
||||||
|
@ -144,7 +143,7 @@ namespace TINK.ViewModel.Map
|
||||||
m_oMoveToRegionDelegate = moveToRegionDelegate
|
m_oMoveToRegionDelegate = moveToRegionDelegate
|
||||||
?? throw new ArgumentException("Can not instantiate map page view model- object. No move delegate available.");
|
?? throw new ArgumentException("Can not instantiate map page view model- object. No move delegate available.");
|
||||||
|
|
||||||
m_oViewService = viewService
|
ViewService = viewService
|
||||||
?? throw new ArgumentException("Can not instantiate map page view model- object. No view available.");
|
?? throw new ArgumentException("Can not instantiate map page view model- object. No view available.");
|
||||||
|
|
||||||
m_oNavigation = navigation
|
m_oNavigation = navigation
|
||||||
|
@ -330,7 +329,7 @@ namespace TINK.ViewModel.Map
|
||||||
|
|
||||||
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
||||||
{
|
{
|
||||||
var dialogResult = await m_oViewService.DisplayAlert(
|
var dialogResult = await ViewService.DisplayAlert(
|
||||||
AppResources.MessageTitleHint,
|
AppResources.MessageTitleHint,
|
||||||
AppResources.MessageCenterMapLocationPermissionOpenDialog,
|
AppResources.MessageCenterMapLocationPermissionOpenDialog,
|
||||||
AppResources.MessageAnswerYes,
|
AppResources.MessageAnswerYes,
|
||||||
|
@ -390,7 +389,7 @@ namespace TINK.ViewModel.Map
|
||||||
Log.ForContext<MapPageViewModel>().Verbose("No pins detected on page.");
|
Log.ForContext<MapPageViewModel>().Verbose("No pins detected on page.");
|
||||||
if (resultStationsAndBikes.Response.StationsAll.CopriVersion < CopriCallsStatic.UnsupportedVersionLower)
|
if (resultStationsAndBikes.Response.StationsAll.CopriVersion < CopriCallsStatic.UnsupportedVersionLower)
|
||||||
{
|
{
|
||||||
await m_oViewService.DisplayAlert(
|
await ViewService.DisplayAlert(
|
||||||
AppResources.MessageWaring,
|
AppResources.MessageWaring,
|
||||||
string.Format(AppResources.MessageCopriVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)),
|
string.Format(AppResources.MessageCopriVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)),
|
||||||
AppResources.MessageAnswerOk);
|
AppResources.MessageAnswerOk);
|
||||||
|
@ -400,7 +399,7 @@ namespace TINK.ViewModel.Map
|
||||||
|
|
||||||
if (resultStationsAndBikes.Response.StationsAll.CopriVersion >= CopriCallsStatic.UnsupportedVersionUpper)
|
if (resultStationsAndBikes.Response.StationsAll.CopriVersion >= CopriCallsStatic.UnsupportedVersionUpper)
|
||||||
{
|
{
|
||||||
await m_oViewService.DisplayAlert(
|
await ViewService.DisplayAlert(
|
||||||
AppResources.MessageWaring,
|
AppResources.MessageWaring,
|
||||||
string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)),
|
string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)),
|
||||||
AppResources.MessageAnswerOk);
|
AppResources.MessageAnswerOk);
|
||||||
|
@ -420,7 +419,7 @@ namespace TINK.ViewModel.Map
|
||||||
Log.ForContext<MapPageViewModel>().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);
|
Log.ForContext<MapPageViewModel>().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.
|
// COPRI reports an auth cookie error.
|
||||||
await m_oViewService.DisplayAlert(
|
await ViewService.DisplayAlert(
|
||||||
AppResources.MessageWaring,
|
AppResources.MessageWaring,
|
||||||
AppResources.MessageMapPageErrorAuthcookieUndefined,
|
AppResources.MessageMapPageErrorAuthcookieUndefined,
|
||||||
AppResources.MessageAnswerOk);
|
AppResources.MessageAnswerOk);
|
||||||
|
@ -464,7 +463,7 @@ namespace TINK.ViewModel.Map
|
||||||
|
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
|
|
||||||
await m_oViewService.DisplayAlert(
|
await ViewService.DisplayAlert(
|
||||||
"Fehler",
|
"Fehler",
|
||||||
$"Beim Anzeigen der Fahrradstandorte- Seite ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
|
$"Beim Anzeigen der Fahrradstandorte- Seite ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
|
||||||
"OK");
|
"OK");
|
||||||
|
@ -621,7 +620,7 @@ namespace TINK.ViewModel.Map
|
||||||
p_strStationName);
|
p_strStationName);
|
||||||
#else
|
#else
|
||||||
// Show page.
|
// Show page.
|
||||||
await m_oViewService.PushAsync(ViewTypes.BikesAtStation);
|
await ViewService.PushAsync(ViewTypes.BikesAtStation);
|
||||||
|
|
||||||
IsMapPageEnabled = true;
|
IsMapPageEnabled = true;
|
||||||
ActionText = "";
|
ActionText = "";
|
||||||
|
@ -632,7 +631,7 @@ namespace TINK.ViewModel.Map
|
||||||
ActionText = "";
|
ActionText = "";
|
||||||
|
|
||||||
Log.ForContext<MapPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {Exception}", exception);
|
Log.ForContext<MapPageViewModel>().Error("Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {Exception}", exception);
|
||||||
await m_oViewService.DisplayAlert(
|
await ViewService.DisplayAlert(
|
||||||
"Fehler",
|
"Fehler",
|
||||||
$"Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {exception.Message}",
|
$"Fehler beim Öffnen der Ansicht \"Fahrräder an Station\" aufgetreten. {exception.Message}",
|
||||||
"OK");
|
"OK");
|
||||||
|
@ -867,7 +866,7 @@ namespace TINK.ViewModel.Map
|
||||||
|
|
||||||
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
|
||||||
{
|
{
|
||||||
var dialogResult = await m_oViewService.DisplayAlert(
|
var dialogResult = await ViewService.DisplayAlert(
|
||||||
AppResources.MessageTitleHint,
|
AppResources.MessageTitleHint,
|
||||||
AppResources.MessageBikesManagementLocationPermission,
|
AppResources.MessageBikesManagementLocationPermission,
|
||||||
"Ja",
|
"Ja",
|
||||||
|
@ -888,7 +887,7 @@ namespace TINK.ViewModel.Map
|
||||||
// see https://github.com/xabre/xamarin-bluetooth-le/issues/112#issuecomment-380994887
|
// see https://github.com/xabre/xamarin-bluetooth-le/issues/112#issuecomment-380994887
|
||||||
if (await BluetoothService.GetBluetoothState() != Plugin.BLE.Abstractions.Contracts.BluetoothState.On)
|
if (await BluetoothService.GetBluetoothState() != Plugin.BLE.Abstractions.Contracts.BluetoothState.On)
|
||||||
{
|
{
|
||||||
await m_oViewService.DisplayAlert(
|
await ViewService.DisplayAlert(
|
||||||
AppResources.MessageTitleHint,
|
AppResources.MessageTitleHint,
|
||||||
AppResources.MessageBikesManagementBluetoothActivation,
|
AppResources.MessageBikesManagementBluetoothActivation,
|
||||||
AppResources.MessageAnswerOk);
|
AppResources.MessageAnswerOk);
|
||||||
|
@ -949,7 +948,7 @@ namespace TINK.ViewModel.Map
|
||||||
{
|
{
|
||||||
Log.ForContext<MapPageViewModel>().Error("An error occurred switching view TINK/ Konrad.{}");
|
Log.ForContext<MapPageViewModel>().Error("An error occurred switching view TINK/ Konrad.{}");
|
||||||
|
|
||||||
await m_oViewService.DisplayAlert(
|
await ViewService.DisplayAlert(
|
||||||
"Fehler",
|
"Fehler",
|
||||||
$"Beim Umschalten TINK/ Konrad ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
|
$"Beim Umschalten TINK/ Konrad ist ein Fehler aufgetreten.\r\n{l_oException.Message}",
|
||||||
"OK");
|
"OK");
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
AgbPage,
|
AgbPage,
|
||||||
WhatsNewPage,
|
WhatsNewPage,
|
||||||
BikesAtStation,
|
BikesAtStation,
|
||||||
ContactPage
|
ContactPage,
|
||||||
|
SelectStationPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue