Initial version.

This commit is contained in:
Oliver Hauff 2021-05-13 20:16:41 +02:00
parent e4fb48f6ab
commit 10dbeb5a90
737 changed files with 61885 additions and 0 deletions

View file

@ -0,0 +1,27 @@
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="TINK.View.Account.AccountPage">
<ContentPage.Content>
<Frame>
<StackLayout>
<Frame>
<StackLayout>
<Label Text="{Binding LoggedInInfo}" />
<Label IsVisible="{Binding IsBookingStateInfoVisible}"
Text="{Binding BookingStateInfo}" />
<Button Text="Persönliche Daten Verwalten"
Command="{Binding OnManageAccount}"
IsEnabled="{Binding IsLogoutPossible}"/>
<Button Text="Abmelden"
Command="{Binding OnLogoutRequest}"
IsEnabled="{Binding IsLogoutPossible}"/>
</StackLayout>
</Frame>
</StackLayout>
</Frame>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,128 @@
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Threading.Tasks;
using TINK.View.MasterDetail;
using System;
using TINK.Model.Device;
using TINK.ViewModel.Account;
namespace TINK.View.Account
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AccountPage : ContentPage, IViewService, IDetailPage
{
/// <summary> Refernce to view model. </summary>
AccountPageViewModel m_oViewModel = null;
/// <summary> Constructs a account page. </summary>
public AccountPage()
{
InitializeComponent();
var l_oModel = App.ModelRoot;
m_oViewModel = new AccountPageViewModel(
l_oModel,
(url) => DependencyService.Get<IExternalBrowserService>().OpenUrl(url),
this);
BindingContext = m_oViewModel;
}
/// <summary> Displays altert message. </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays alert message.</summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
/// <summary>
/// Displays an action sheet.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <param name="destruction"></param>
/// <param name="p_oButtons">Buttons holding options to select.</param>
/// <returns>Text selected</returns>
public new async Task<string> DisplayActionSheet(String p_strTitle, String p_strCancel, String destruction, params String[] p_oButtons)
=> await base.DisplayActionSheet(p_strTitle, p_strCancel, destruction, p_oButtons);
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
=> throw new NotImplementedException();
/// <summary> Pops a page from the modal stack. </summary>
public Task PopModalAsync()
=> throw new NotSupportedException();
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigation;
/// <summary>
/// Delegate to perform navigation.
/// </summary>
public INavigationMasterDetail NavigationMasterDetail
{
set { m_oNavigation = value; }
}
/// <summary>
/// Invoked when page is shown.
/// Starts update process.
/// </summary>
protected async override void OnAppearing()
=> await m_oViewModel.OnAppearing();
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
protected async override void OnDisappearing()
{
if (m_oViewModel == null)
{
// View model might be null.
return;
}
await m_oViewModel.OnDisappearing();
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes p_oTypeOfPage)
=> await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
}
}

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:conv="clr-namespace:TINK.View"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
mc:Ignorable="d"
x:Class="TINK.View.Bike.BCBike">
<ContentView>
<ContentView.Resources>
<conv:StringNotNullOrEmptyToVisibleConverter x:Key="Label_Converter"/>
</ContentView.Resources>
<StackLayout
Padding="10">
<Label
FontAttributes="Bold"
Text="{Binding Name}"/>
<Label
Text="{Binding StateText}"
TextColor="{Binding StateColor}"/>
<Label
Text="{Binding ErrorText}"
IsVisible="{Binding ErrorText, Converter={StaticResource Label_Converter}}"
TextColor="Red"/>
<Button
Text="{Binding ButtonText}"
IsVisible="{Binding IsButtonVisible}"
IsEnabled="{Binding IsIdle}"
Command="{Binding OnButtonClicked}"/>
<Grid
IsVisible="{Binding TariffDescription.Header, Converter={StaticResource Label_Converter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Text=
"{Binding TariffDescription.Header}"
Grid.ColumnSpan="3"
FontAttributes="Bold"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionFreeTimePerSession}"
IsVisible="{Binding TariffDescription.FreeTimePerSession, Converter={StaticResource Label_Converter}}"
Grid.Row="1"/>
<Label
Text="{Binding TariffDescription.FreeTimePerSession}"
IsVisible="{Binding TariffDescription.FreeTimePerSession, Converter={StaticResource Label_Converter}}"
Grid.Row="1"
Grid.Column="1"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionFeeEuroPerHour}"
IsVisible="{Binding TariffDescription.FeeEuroPerHour, Converter={StaticResource Label_Converter}}"
Grid.Row="2"/>
<Label
Text="{Binding TariffDescription.FeeEuroPerHour}"
IsVisible="{Binding TariffDescription.FeeEuroPerHour, Converter={StaticResource Label_Converter}}"
Grid.Row="2"
Grid.Column="1"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionMaxFeeEuroPerDay}"
IsVisible="{Binding TariffDescription.MaxFeeEuroPerDay, Converter={StaticResource Label_Converter}}"
Grid.Row="3"/>
<Label
Text="{Binding TariffDescription.MaxFeeEuroPerDay}"
IsVisible="{Binding TariffDescription.MaxFeeEuroPerDay, Converter={StaticResource Label_Converter}}"
Grid.Row="3"
Grid.Column="1"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionAboEuroPerMonth}"
IsVisible="{Binding TariffDescription.AboEuroPerMonth, Converter={StaticResource Label_Converter}}"
Grid.Row="4"/>
<Label
Text="{Binding TariffDescription.AboEuroPerMonth}"
IsVisible="{Binding TariffDescription.AboEuroPerMonth, Converter={StaticResource Label_Converter}}"
Grid.Row="4"
Grid.Column="1"/>
</Grid>
</StackLayout>
</ContentView>
</ViewCell>

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Bike
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BCBike : ViewCell
{
public BCBike()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,26 @@
using Xamarin.Forms;
namespace TINK.View.Bike
{
/// <summary>
/// Selects different templates for different bike types (BordComputer bikes, iLockIt bikes).
/// </summary>
public class BikeViewCellTemplateSelector : DataTemplateSelector
{
DataTemplate bCBike;
DataTemplate iLockIBike;
public BikeViewCellTemplateSelector()
{
bCBike = new DataTemplate(typeof(BCBike));
iLockIBike = new DataTemplate(typeof(ILockItBike));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return item is TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel
? iLockIBike
: bCBike;
}
}
}

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:conv="clr-namespace:TINK.View"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
mc:Ignorable="d"
x:Class="TINK.View.Bike.ILockItBike">
<ContentView>
<ContentView.Resources>
<conv:StringNotNullOrEmptyToVisibleConverter x:Key="Label_Converter"/>
</ContentView.Resources>
<StackLayout
Padding="10">
<Label
FontAttributes="Bold"
Text="{Binding Name}"/>
<Label
Text="{Binding StateText}"
TextColor="{Binding StateColor}"/>
<Label
Text="{Binding ErrorText}"
IsVisible="{Binding ErrorText, Converter={StaticResource Label_Converter}}"
TextColor="Red"/>
<Button
Text="{Binding ButtonText}"
IsVisible="{Binding IsButtonVisible}"
IsEnabled="{Binding IsIdle}"
Command="{Binding OnButtonClicked}"/>
<Button
Text="{Binding LockitButtonText}"
IsVisible="{Binding IsLockitButtonVisible}"
IsEnabled="{Binding IsIdle}"
Command="{Binding OnLockitButtonClicked}"/>
<Grid
IsVisible="{Binding TariffDescription.Header, Converter={StaticResource Label_Converter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Text=
"{Binding TariffDescription.Header}"
Grid.ColumnSpan="3"
FontAttributes="Bold"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionFreeTimePerSession}"
IsVisible="{Binding TariffDescription.FreeTimePerSession, Converter={StaticResource Label_Converter}}"
Grid.Row="1"/>
<Label
Text="{Binding TariffDescription.FreeTimePerSession}"
IsVisible="{Binding TariffDescription.FreeTimePerSession, Converter={StaticResource Label_Converter}}"
Grid.Row="1"
Grid.Column="1"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionFeeEuroPerHour}"
IsVisible="{Binding TariffDescription.FeeEuroPerHour, Converter={StaticResource Label_Converter}}"
Grid.Row="2"/>
<Label
Text="{Binding TariffDescription.FeeEuroPerHour}"
IsVisible="{Binding TariffDescription.FeeEuroPerHour, Converter={StaticResource Label_Converter}}"
Grid.Row="2"
Grid.Column="1"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionMaxFeeEuroPerDay}"
IsVisible="{Binding TariffDescription.MaxFeeEuroPerDay, Converter={StaticResource Label_Converter}}"
Grid.Row="3"/>
<Label
Text="{Binding TariffDescription.MaxFeeEuroPerDay}"
IsVisible="{Binding TariffDescription.MaxFeeEuroPerDay, Converter={StaticResource Label_Converter}}"
Grid.Row="3"
Grid.Column="1"/>
<Label
Text="{x:Static resources:AppResources.MessageBikesManagementTariffDescriptionAboEuroPerMonth}"
IsVisible="{Binding TariffDescription.AboEuroPerMonth, Converter={StaticResource Label_Converter}}"
Grid.Row="4"/>
<Label
Text="{Binding TariffDescription.AboEuroPerMonth}"
IsVisible="{Binding TariffDescription.AboEuroPerMonth, Converter={StaticResource Label_Converter}}"
Grid.Row="4"
Grid.Column="1"/>
</Grid>
</StackLayout>
</ContentView>
</ViewCell>

View file

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TINK.Model.Bikes.Bike.BluetoothLock;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Bike
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ILockItBike : ViewCell
{
public ILockItBike()
{
InitializeComponent();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (Device.RuntimePlatform != Device.iOS)
// Update of size is only required for iOS.
return;
var viewModel = BindingContext as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel;
if (viewModel == null)
return;
viewModel.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == nameof(TINK.ViewModel.Bikes.Bike.BC.RequestHandler.Base<IBikeInfoMutable>.IsButtonVisible)
|| e.PropertyName == nameof(TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler.Base.IsLockitButtonVisible))
{
// Force update of view cell on iOS.
// https://hausource.visualstudio.com/TINK/_workitems/edit/132
ForceUpdateSize();
}
};
}
}
}

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.BikesAtStation.BikesAtStationPage"
xmlns:local_bike="clr-namespace:TINK.View.Bike"
Title="{Binding Title}">
<ContentPage.Resources>
<ResourceDictionary>
<local_bike:BikeViewCellTemplateSelector x:Key="bikeTemplateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<Frame>
<StackLayout>
<ListView
x:Name="BikesAtStationListView"
SelectionMode="None"
SelectedItem="{Binding SelectedBike}"
IsEnabled="{Binding IsIdle}"
IsVisible="{Binding IsBikesListVisible}"
HasUnevenRows="True"
ItemTemplate="{StaticResource bikeTemplateSelector}"/>
<StackLayout
VerticalOptions="EndAndExpand"
Orientation="Horizontal">
<Label
IsVisible="{Binding IsNoBikesAtStationVisible}"
VerticalOptions="EndAndExpand"
Text="{Binding NoBikesAtStationText}"/>
<Label
IsVisible="{Binding IsLoginRequiredHintVisible}"
FormattedText="{Binding LoginRequiredHintText}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding LoginRequiredHintClickedCommand}"/>
</Label.GestureRecognizers>
</Label>
<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>
</Frame>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,207 @@

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TINK.Model.Bike.BluetoothLock;
namespace TINK.View.BikesAtStation
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Device;
using TINK.View.MasterDetail;
using TINK.ViewModel;
using TINK.Model;
using TINK.Services.BluetoothLock.Tdo;
using System.Collections.Generic;
using Serilog;
using TINK.Services.BluetoothLock;
using Plugin.BLE;
using TINK.ViewModel.BikesAtStation;
using TINK.ViewModel.Bikes;
using Xamarin.CommunityToolkit.Extensions;
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BikesAtStationPage : ContentPage, IViewService, IDetailPage
{
private BikesAtStationPageViewModel m_oViewModel;
#if TRYNOTBACKSTYLE
public BikesAtStationPage()
{
InitializeComponent();
var l_oModel = App.ModelRoot;
var l_oViewModel = new BikesAtStationPageViewModel(
l_oModel.BikesAtStation,
l_oModel.ActiveUser,
l_oModel.SelectedStation,
this);
BindingContext = l_oViewModel;
BikesAtStationListView.ItemsSource = l_oViewModel;
}
#else
public BikesAtStationPage()
{
}
#endif
/// <summary>
/// Invoked when page is shown.
/// Starts update process.
/// </summary>
protected async override void OnAppearing()
{
if (m_oViewModel != null)
{
#if BACKSTYLE
// Hide master- detail menu to force user to navigate using back button.
m_oNavigation.IsGestureEnabled = false;
#endif
// No need to create view model, set binding context an items source if already done.
// If done twice tap events are fired multiple times (when hiding page using home button).
await m_oViewModel.OnAppearing();
return;
}
try
{
var model = App.ModelRoot;
// Backup synchronization context when called from GUI-thread.
var synchronizationContext = SynchronizationContext.Current;
m_oViewModel = new BikesAtStationPageViewModel(
model.ActiveUser,
model.Permissions,
CrossBluetoothLE.Current,
Device.RuntimePlatform,
model.SelectedStation,
() => model.GetIsConnected(),
(isConnected) => model.GetConnector(isConnected),
model.Geolocation,
model.LocksServices.Active,
model.Polling,
(url) => DependencyService.Get<IExternalBrowserService>().OpenUrl(url),
(d, obj) => synchronizationContext.Post(d, obj),
this);
}
catch (Exception exception)
{
Log.ForContext<BikesAtStationPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
this.DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
return;
}
InitializeComponent();
#if BACKSTYLE
// Hide master- detail menu to force user to navigate using back button.
m_oNavigation.IsGestureEnabled = false;
#endif
BindingContext = m_oViewModel;
BikesAtStationListView.ItemsSource = m_oViewModel;
await m_oViewModel.OnAppearing();
}
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
protected async override void OnDisappearing()
{
if (m_oViewModel != null)
{
// View model might be null.
await m_oViewModel?.OnDisappearing();
}
#if BACKSTYLE
if (m_oNavigation!= null)
m_oNavigation.IsGestureEnabled = true; // Enables master- detail menu navigation again when page is unloaded.
#endif
}
/// <summary> Displays altert message.</summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
{
await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary>
/// Displays alert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
{
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
/// <summary> Creates and a page an shows it.</summary>
/// <remarks> When user is not logged in navigation to Login page is supported.</remarks>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
}
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
throw new NotSupportedException();
}
/// <summary> Pops a page from the modal stack. </summary>
public Task PopModalAsync()
{
throw new NotSupportedException();
}
public Task PushAsync(ViewTypes p_oTypeOfPage)
{
throw new NotImplementedException();
}
/// <summary>
/// Delegate to perform navigation.
/// </summary>
private INavigationMasterDetail m_oNavigation;
/// <summary>
/// Delegate to perform navigation.
/// </summary>
public INavigationMasterDetail NavigationMasterDetail
{
set { m_oNavigation = value; }
}
public async Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
}
}

View file

@ -0,0 +1,38 @@
<?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:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
x:Class="TINK.View.Contact.ContactPage"
Title="Kontakt">
<ContentPage.Content>
<ScrollView>
<Frame>
<StackLayout x:Name="ContactPageView">
<Frame>
<StackLayout>
<Label FormattedText="{Binding MaliAddressAndMotivationsText}"/>
<Button Text="{Binding MailAddressText}"
IsEnabled="{Binding IsSendMailAvailable}"
Command="{Binding OnMailRequest}"/>
</StackLayout>
</Frame>
<Frame>
<StackLayout>
<Label FormattedText="{Binding PhoneContactText}"/>
<Button Text="{Binding PhoneNumberText}"
IsEnabled="{Binding IsDoPhoncallAvailable}"
Command="{Binding OnPhoneRequest}"/>
</StackLayout>
</Frame>
<Frame>
<StackLayout>
<Label FormattedText="{Binding LikeTinkApp}"/>
<Button Text="{x:Static resources:AppResources.ActionContactRate}"
Command="{Binding OnRateRequest}"/>
</StackLayout>
</Frame>
</StackLayout>
</Frame>
</ScrollView>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,68 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Device;
using TINK.ViewModel.Info;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Contact
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ContactPage : ContentPage, IViewService
{
public ContactPage ()
{
InitializeComponent ();
ContactPageView.BindingContext = new ContactPageViewModel(
App.ModelRoot.Uris.ActiveUri,
() => App.CreateAttachment(),
() => DependencyService.Get<IExternalBrowserService>().OpenUrl(DependencyService.Get<IAppInfo>().StoreUrl),
this);
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotSupportedException();
}
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
throw new NotSupportedException();
}
/// <summary> Pops a page from the modal stack. </summary>
public Task PopModalAsync()
{
throw new NotSupportedException();
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushAsync(ViewTypes p_oTypeOfPage)
{
throw new NotImplementedException();
}
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.CopriWebView.ManageAccountPage">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackLayout>
<WebView x:Name="ManageAccount"
HeightRequest="1400"
WidthRequest="1000"
Source="{Binding Uri}" />
</StackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,42 @@
using TINK.Model.Device;
using TINK.ViewModel.Login;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.CopriWebView
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ManageAccountPage : ContentPage
{
public ManageAccountPage ()
{
InitializeComponent ();
ManageAccount.Navigating += (sender, ev) =>
{
if (!ev.Url.ToUpper().EndsWith(".PDF"))
{
// Stay inside web view except for downloading pdf- files.
return;
}
DependencyService.Get<IExternalBrowserService>().OpenUrl(ev.Url);
};
ManageAccount.Navigated += (sender, ev) =>
{
if (ev.Result == WebNavigationResult.Success) return;
ManageAccount.Source = new HtmlWebViewSource
{
Html = "<html><b>Kann persönliche Daten nicht anzeigen/ verwalten!</b><br>Verbindung mit Internet ok?</html>"
};
};
ManageAccount.BindingContext = new ManageAccountViewModel(
App.ModelRoot.ActiveUser.SessionCookie,
Model.TinkApp.MerchantId,
App.ModelRoot.NextActiveUri.Host);
}
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.CopriWebView.PasswordForgottenPage">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackLayout>
<WebView x:Name="PasswordForgottenWebView"
HeightRequest="1400"
WidthRequest="1000"
Source="{Binding Uri}" />
</StackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,30 @@

using TINK.ViewModel.CopriWebView;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.CopriWebView
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class PasswordForgottenPage : ContentPage
{
public PasswordForgottenPage ()
{
InitializeComponent ();
PasswordForgottenWebView.Navigated += (sender, ev) =>
{
if (ev.Result == WebNavigationResult.Success) return;
PasswordForgottenWebView.Source = new HtmlWebViewSource
{
Html = "<html><b>Kann Passwort vergessen Seite nicht anzeigen!</b><br>Verbindung mit Internet ok?</html>"
};
};
PasswordForgottenWebView.BindingContext = new PasswordForgottonViewModel(
Model.TinkApp.MerchantId,
App.ModelRoot.NextActiveUri.Host);
}
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.CopriWebView.RegisterPage">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackLayout>
<WebView x:Name="RegisterView"
HeightRequest="1400"
WidthRequest="1000"
Source="{Binding Uri}" />
</StackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,33 @@

using TINK.Model.Device;
using TINK.ViewModel.CopriWebView;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.CopriWebView
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RegisterPage : ContentPage
{
public RegisterPage()
{
DependencyService.Get<IWebView>().ClearCookies();
InitializeComponent();
RegisterView.Navigated += (sender, ev) =>
{
if (ev.Result == WebNavigationResult.Success) return;
RegisterView.Source = new HtmlWebViewSource
{
Html = "<html><b>Kann Anmeldeseite nicht anzeigen</b>!<br>Verbindung mit Internet ok?</html>"
};
};
RegisterView.BindingContext = new RegisterPageViewModel(
App.ModelRoot.NextActiveUri.Host);
}
}
}

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8" ?>
<xct:Popup xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:local="clr-namespace:TINK.View"
x:TypeArguments="local:FeedbackPopup+Result"
x:Class="TINK.View.FeedbackPopup">
<xct:Popup.Resources>
<x:String x:Key="check_circle">&#xf058;</x:String>
</xct:Popup.Resources>
<Grid>
<Grid.RowDefinitions>
<!-- Head and title row -->
<RowDefinition Height="auto"/>
<!--- checkbox and input elements-->
<RowDefinition Height="*"/>
<!--- ok button-->
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid
Padding="30"
BackgroundColor="{DynamicResource primary-back-title-color}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label
HorizontalTextAlignment="Center"
FontSize="Large"
Text="Fahrrad erfolgreich zurückgegeben!"/>
<Image Grid.Row="1">
<Image.Source>
<FontImageSource Size="Header" Glyph="{StaticResource check_circle}" FontFamily="FA-S"/>
</Image.Source>
</Image>
</Grid>
<ScrollView Grid.Row="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout
Orientation="Vertical">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox x:Name="brockenCheckBox" IsChecked="True"/>
<Label
Grid.Column="1"
FontSize="Medium"
Text="Rad ist in Ordnung"/>
</Grid>
<Editor
Grid.Row="1"
x:Name="feedbackMessage"
AutoSize="TextChanges"
Placeholder="Bei Bedarf bitte hier Rückmeldung eingeben."
Text="">
<Editor.Triggers>
<DataTrigger TargetType="Editor"
Binding="{Binding Source={x:Reference brockenCheckBox}, Path=IsChecked}"
Value="true">
<Setter Property="Placeholder"
Value="Bei Bedarf bitte hier Rückmeldung eingeben." />
</DataTrigger>
<DataTrigger TargetType="Editor"
Binding="{Binding Source={x:Reference brockenCheckBox}, Path=IsChecked}"
Value="false">
<Setter Property="Placeholder"
Value="Bitte Zustand/ Defekt hier beschreiben." />
</DataTrigger>
</Editor.Triggers>
</Editor>
</StackLayout>
</Grid>
</ScrollView>
<Button
Grid.Row="2"
Clicked="OnOkClicked"
Text="OK"/>
</Grid>
</xct:Popup>

View file

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.CommunityToolkit.UI.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FeedbackPopup : Popup<FeedbackPopup.Result>
{
public FeedbackPopup ()
{
InitializeComponent ();
}
protected override FeedbackPopup.Result GetLightDismissResult()
{
return new Result
{
Message = feedbackMessage.Text,
IsBikeBroken = brockenCheckBox.IsChecked
};
}
private void OnOkClicked(object sender, EventArgs eventArgs)
{
var result = new Result
{
Message = feedbackMessage.Text,
IsBikeBroken = brockenCheckBox.IsChecked
};
base.Dismiss(result);
}
/// <summary>
/// Feedback given by user when returning bike.
/// </summary>
public class Result : IViewService.IUserFeedback
{
/// <summary>
/// Holds whether bike is broken or not.
/// </summary>
public bool IsBikeBroken { get; set; }
/// <summary>
/// Holds either
/// - general feedback
/// - error description of broken bike
/// or both.
/// </summary>
public string Message { get; set; }
}
}
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
x:Class="TINK.View.Contact.FeesAndBikesPage">
<!--Pages can be added as references or inline-->
<ContentPage Title="{x:Static resources:AppResources.MarkingTabFees}">
<ContentPage.Content>
<StackLayout>
<WebView
x:Name="InfoRentBikeWebView"
HeightRequest="1000"
WidthRequest="1000"
Source="{Binding RentBikeText}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
<ContentPage Title="{x:Static resources:AppResources.MarkingTabBikes}">
<ContentPage.Content>
<StackLayout>
<WebView
x:Name="InfoTypesOfBikesWebView"
HeightRequest="1000"
WidthRequest="1000"
Source="{Binding TypesOfBikesText}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
</TabbedPage>

View file

@ -0,0 +1,36 @@
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TINK.ViewModel.Contact;
using TINK.Model;
namespace TINK.View.Contact
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FeesAndBikesPage : TabbedPage
{
public HelpContactViewModel ViewModel { get; }
public FeesAndBikesPage ()
{
InitializeComponent();
ViewModel = new HelpContactViewModel(
TINK.App.ModelRoot.NextActiveUri.Host,
TINK.App.ModelRoot.IsSiteCachingOn);
BindingContext = ViewModel;
/// Info about renting.
InfoRentBikeWebView.Navigating += ViewModelHelper.OnNavigating;
/// Info about types of bikes.
InfoTypesOfBikesWebView.Navigating += ViewModelHelper.OnNavigating;
}
/// <summary> Called when page is shown. </summary>
protected override void OnAppearing()
{
ViewModel.OnAppearing();
}
}
}

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8" ?>
<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.Info.BikeInfo.BikeInfoCarouselPage">
<CarouselPage.ItemTemplate>
<DataTemplate>
<ContentPage>
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,40,0,0" />
</OnPlatform>
</ContentPage.Padding>
<ScrollView Orientation="Vertical">
<StackLayout>
<Label
Text="{Binding Title}"
FontSize="Medium"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
VerticalOptions="EndAndExpand"
VerticalTextAlignment="End"/>
<Image
IsVisible="{Binding IsImageVisble}"
Source="{Binding Image}"
Aspect="AspectFit"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"/>
<Label
Text="{Binding DescriptionText}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
VerticalOptions="StartAndExpand"
VerticalTextAlignment="Start"/>
<Button
Text="Schließen"
IsVisible="{Binding IsCloseVisible}"
Command="{Binding OnCloseRequest}"
VerticalOptions="End"/>
<ProgressBar
VerticalOptions="End"
Progress="{Binding ProgressValue}"/>
</StackLayout>
</ScrollView>
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
</CarouselPage>

View file

@ -0,0 +1,103 @@
using System;
using System.Threading.Tasks;
using TINK.View.MasterDetail;
using TINK.ViewModel;
using TINK.ViewModel.Info.BikeInfo;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Info.BikeInfo
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BikeInfoCarouselPage : CarouselPage, IViewService, IDetailPage
{
public BikeInfoCarouselPage ()
{
InitializeComponent ();
ItemsSource = new BikeInfoViewModel(
resourceName => ImageSource.FromResource($"{ViewModelResourceHelper.RessourcePrefix}Images.{resourceName}"),
this).CarouselItems;
}
/// <summary>
/// Displays altert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
{
await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary>
/// Displays alert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
{
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
}
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
throw new NotSupportedException();
}
/// <summary> Pops a page from the modal stack. </summary>
public Task PopModalAsync()
{
throw new NotSupportedException();
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushAsync(ViewTypes p_oTypeOfPage)
{
throw new NotImplementedException();
}
/// <summary>
/// Delegate to perform navigation.
/// </summary>
private INavigationMasterDetail m_oNavigation;
/// <summary>
/// Delegate to perform navigation.
/// </summary>
public INavigationMasterDetail NavigationMasterDetail
{
set { m_oNavigation = value; }
}
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
}
}

View file

@ -0,0 +1,40 @@
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class InfoPage : ContentPage
{
public InfoPage ()
{
InitializeComponent ();
#if __IOS__
var resourcePrefix = "TINK.iOS.";
#endif
#if __ANDROID__
var resourcePrefix = "TINK.Droid.";
#endif
#if WINDOWS_PHONE
var resourcePrefix = "TINK.WinPhone.";
#endif
Debug.WriteLine("Using this resource prefix: " + resourcePrefix);
// note that the prefix includes the trailing period '.' that is required
var assembly = typeof(InfoPage).GetTypeInfo().Assembly;
Stream stream = assembly.GetManifestResourceStream
(resourcePrefix + "HtmlResouces.Info.html");
var l_oHtmlViewSource = new HtmlWebViewSource
{
Html = (new StreamReader(stream, Encoding.UTF8)).ReadToEnd()
};
InfoWebView.Source = l_oHtmlViewSource;
}
}
}

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Info
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class InfoPage : ContentPage
{
public InfoPage ()
{
InitializeComponent ();
}
}
}

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.Info.TabbedPageInfo"
x:Name="TabbedInfoPage">
<!--Pages can be added as references or inline-->
<ContentPage Title="App">
<ContentPage.Content>
<StackLayout>
<WebView
x:Name="InfoLicenses"
Source="{Binding InfoLicenses}"
HeightRequest="1000"
WidthRequest="1000" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
<ContentPage Title="Datenschutz">
<ContentPage.Content>
<StackLayout>
<WebView
x:Name="InfoDatenschutz"
Source="{Binding InfoPrivacy}"
HeightRequest="1000"
WidthRequest="1000" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
<ContentPage Title="AGB">
<ContentPage.Content>
<StackLayout>
<WebView
x:Name="InfoABG"
Source ="{Binding InfoAgb}"
HeightRequest="1000"
WidthRequest="1000" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
<ContentPage Title="Impressum">
<ContentPage.Content>
<StackLayout>
<WebView
x:Name="InfoImpressum"
Source="{Binding InfoImpressum}"
HeightRequest="1000"
WidthRequest="1000" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
</TabbedPage>

View file

@ -0,0 +1,36 @@
using TINK.ViewModel;
using TINK.ViewModel.Info;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Info
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TabbedPageInfo : TabbedPage
{
public InfoViewModel ViewModel { get; }
public TabbedPageInfo()
{
InitializeComponent();
ViewModel = new InfoViewModel(
App.ModelRoot.NextActiveUri.Host,
App.ModelRoot.IsSiteCachingOn,
resourceName => ViewModelResourceHelper.GetSource(resourceName));
TabbedInfoPage.BindingContext = ViewModel;
InfoLicenses.Navigating += ViewModelHelper.OnNavigating;
InfoDatenschutz.Navigating += ViewModelHelper.OnNavigating;
InfoABG.Navigating += ViewModelHelper.OnNavigating;
InfoImpressum.Navigating += ViewModelHelper.OnNavigating;
}
/// <summary> Called when page is shown. </summary>
protected override void OnAppearing()
{
ViewModel.OnAppearing();
}
}
}

View file

@ -0,0 +1,35 @@
using System.Windows.Input;
using Xamarin.Forms;
namespace TINK.View
{
public static class ListViewAttachedBehavior
{
public static readonly BindableProperty CommandProperty =
BindableProperty.CreateAttached(
"Command",
typeof(ICommand),
typeof(ListViewAttachedBehavior),
null,
propertyChanged: OnCommandChanged);
static void OnCommandChanged(BindableObject view, object oldValue, object newValue)
{
var entry = view as ListView;
if (entry == null)
return;
entry.ItemTapped += (sender, e) =>
{
var command = (newValue as ICommand);
if (command == null)
return;
if (command.CanExecute(e.Item))
{
command.Execute(e.Item);
}
};
}
}
}

View file

@ -0,0 +1,53 @@
<?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:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
x:Class="TINK.View.Login.LoginPage">
<ScrollView>
<Frame>
<StackLayout x:Name="LoginPageView">
<Frame>
<StackLayout>
<Label Text="{x:Static resources:AppResources.MarkingLoginEmailAddressLabel}"/>
<Entry
Placeholder="{x:Static resources:AppResources.MarkingLoginEmailAddressPlaceholder}"
Keyboard="Email"
AutomationId="mail_address_text"
x:Name="EMailEntry"
Text="{Binding MailAddress}"
IsEnabled="{Binding IsLoggedOut}"/>
<Label Text="{x:Static resources:AppResources.MarkingLoginPasswordLabel}"/>
<Entry
Placeholder="{x:Static resources:AppResources.MarkingLoginPasswordPlaceholder}"
AutomationId="password_text"
IsPassword="true"
x:Name="PasswordEntry"
Text="{Binding Password}"
IsEnabled="{Binding IsLoggedOut}"/>
<Button
Text="{x:Static resources:AppResources.ActionLoginLogin}"
AutomationId="login_button"
Command="{Binding OnLoginRequest}"
IsEnabled="{Binding IsLoginRequestAllowed}">
</Button>
<Button
Text="{x:Static resources:AppResources.ActionLoginRegister}"
AutomationId="register_button"
Command="{Binding OnRegisterRequest}"
IsVisible="{Binding IsWebViewElementsVisible}">
</Button>
<Label
IsVisible="{Binding IsRegisterTargetsInfoVisible}"
FormattedText="{Binding RegisterTargetsInfo}">
</Label>
<Button
Text="{x:Static resources:AppResources.ActionLoginPasswordForgotten}"
AutomationId="password_forgotten_button"
Command="{Binding OnPasswordForgottonRequest}">
</Button>
</StackLayout>
</Frame>
</StackLayout>
</Frame>
</ScrollView>
</ContentPage>

View file

@ -0,0 +1,98 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Device;
using TINK.View.MasterDetail;
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Login
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage, IViewService, IDetailPage
{
public LoginPage ()
{
InitializeComponent ();
var l_oModel = App.ModelRoot;
#if !BACKSTYLE
var l_oViewModel = new LoginPageViewModel(
l_oModel,
(url) => DependencyService.Get< IExternalBrowserService>().OpenUrl(url),
this);
LoginPageView.BindingContext = l_oViewModel;
#else
LoginPageView.BindingContext = new LoginPageViewModel(l_oModel.ActiveUser, this, Navigation);
#endif
}
/// <summary>
/// Displays altert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
{
await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
}
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
throw new NotSupportedException();
}
/// <summary> Pops a page from the modal stack. </summary>
public Task PopModalAsync()
{
throw new NotSupportedException();
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes p_oTypeOfPage)
{
await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
}
/// <summary>
/// Delegate to perform navigation.
/// </summary>
private INavigationMasterDetail m_oNavigation;
/// <summary>
/// Delegate to perform navigation.
/// </summary>
public INavigationMasterDetail NavigationMasterDetail
{
set { m_oNavigation = value; }
}
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
}
}

View file

@ -0,0 +1,69 @@
<?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.Map.MapPage"
Title="{x:Static resources:AppResources.MarkingMapPage}">
<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>
<Button
x:Name="TINKButton"
AutomationId ="FilterTINK_button"
Text="TINK"
Command="{Binding OnToggleKonradToTink}"
IsVisible="{Binding IsToggleVisible}"
TextColor ="{Binding TinkColor}"
VerticalOptions="Start"
HorizontalOptions="StartAndExpand"
WidthRequest="80">
</Button>
<Button
x:Name="KonradButton"
AutomationId ="FilterKonrad_button"
Text="Konrad"
Command="{Binding OnToggleTinkToKonrad}"
IsVisible="{Binding IsToggleVisible}"
TextColor="{Binding KonradColor}"
VerticalOptions="Start"
HorizontalOptions="EndAndExpand"
WidthRequest="80">
</Button>
</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>

View file

@ -0,0 +1,172 @@
using Plugin.Connectivity;
using System;
using System.Threading.Tasks;
using TINK.View.MasterDetail;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Map
{
using TINK.Model.Services.CopriApi.ServerUris;
using TINK.ViewModel.Map;
using Xamarin.Forms.GoogleMaps;
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MapPage : ContentPage, IViewService, IDetailPage
{
/// <summary> View model to notify about whether page appears or hides. </summary>
private MapPageViewModel m_oMapPageViewModel;
/// <summary>
/// Constructs map page instance.
/// </summary>
public MapPage()
{
InitializeComponent();
}
/// <summary>
/// Displays altert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
{
await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary>
/// Displays alert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
{
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
m_oNavigationMasterDetail.ShowPage(p_oType.GetViewType(), p_strTitle);
}
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Type of page to display.</param>
public async Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
await Navigation.PushModalAsync((Page)Activator.CreateInstance(p_oTypeOfPage.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="p_oTypeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes p_oTypeOfPage)
{
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType()) as IDetailPage;
if (page == null)
{
return;
}
page.NavigationMasterDetail = m_oNavigationMasterDetail;
await Navigation.PushAsync((Page)page);
}
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
/// <summary> Delegate to perform navigation. </summary>
private INavigationMasterDetail m_oNavigationMasterDetail;
/// <summary> Delegate to perform navigation.</summary>
public INavigationMasterDetail NavigationMasterDetail
{
set
{
m_oNavigationMasterDetail = value;
}
}
/// <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.
#if TRYNOTBACKSTYLE
m_oMapPageViewModel = new MapPageViewModel();
#else
m_oMapPageViewModel = new MapPageViewModel(
App.ModelRoot,
(mapspan) => MyMap.MoveToRegion(mapspan),
this,
Navigation);
#endif
BindingContext = m_oMapPageViewModel;
if (Device.RuntimePlatform == Device.iOS)
{
TINKButton.BackgroundColor = Color.LightGray;
TINKButton.BorderColor = Color.Black;
TINKButton.Margin = new Thickness(10, 10, 10, 10);
KonradButton.BackgroundColor = Color.LightGray;
KonradButton.BorderColor = Color.Black;
KonradButton.Margin = new Thickness(10, 10, 10, 10);
}
m_oMapPageViewModel.NavigationMasterDetail = m_oNavigationMasterDetail;
base.OnAppearing();
// Pre move and scanle maps to avoid initial display of map in Rome.
MapPageViewModel.MoveAndScale(
(mapSpan) => MyMap.MoveToRegion(mapSpan),
App.ModelRoot.Uris.ActiveUri,
App.ModelRoot.GroupFilterMapPage);
await m_oMapPageViewModel.OnAppearing();
}
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
protected override async void OnDisappearing()
{
if (m_oMapPageViewModel != null)
{
// View model might be null.
await m_oMapPageViewModel?.OnDisappearing();
}
base.OnDisappearing();
}
}
}

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.MyBikes.MyBikesPage"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
xmlns:local_bike="clr-namespace:TINK.View.Bike"
Title="{x:Static resources:AppResources.MarkingMyBikes}">
<ContentPage.Resources>
<ResourceDictionary>
<local_bike:BikeViewCellTemplateSelector x:Key="bikeTemplateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<Frame>
<StackLayout>
<ListView
x:Name="MyBikesListView"
SelectionMode="None"
SelectedItem="{Binding SelectedBike}"
IsEnabled="{Binding IsIdle}"
IsVisible="{Binding IsBikesListVisible}"
HasUnevenRows="True"
ItemTemplate="{StaticResource bikeTemplateSelector}"/>
<StackLayout
VerticalOptions="EndAndExpand"
Orientation="Horizontal">
<Label
IsVisible="{Binding IsNoBikesOccupiedVisible}"
VerticalOptions="StartAndExpand"
Text="{Binding NoBikesOccupiedText}"/>
<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>
</Frame>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,170 @@
using Plugin.Connectivity;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Bike.BluetoothLock;
using TINK.View.MasterDetail;
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.MyBikes
{
using Plugin.Permissions.Abstractions;
using Plugin.Permissions;
using TINK.Model;
using TINK.Services.BluetoothLock.Tdo;
using System.Collections.Generic;
using Serilog;
using Plugin.BLE;
using TINK.Services.BluetoothLock;
using TINK.ViewModel.MyBikes;
using Xamarin.CommunityToolkit.Extensions;
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MyBikesPage : ContentPage, IViewService
{
/// <summary> Refernce to view model. </summary>
MyBikesPageViewModel m_oViewModel = null;
/// <summary>
/// Constructs a my bikes page.
/// </summary>
public MyBikesPage()
{
}
/// <summary>
/// Invoked when page is shown.
/// Starts update process.
/// </summary>
protected async override void OnAppearing()
{
if (m_oViewModel != null)
{
// No need to create view model, set binding context an items source if already done.
// If done twice tap events are fired multiple times (when hiding page using home button).
await m_oViewModel.OnAppearing();
return;
}
try
{
var model = App.ModelRoot;
// Backup synchronization context when called from GUI-thread.
var synchronizationContext = SynchronizationContext.Current;
m_oViewModel = new MyBikesPageViewModel(
model.ActiveUser,
model.Permissions,
CrossBluetoothLE.Current,
Device.RuntimePlatform,
() => model.GetIsConnected(),
(isConnected) => model.GetConnector(isConnected),
model.Geolocation,
model.LocksServices.Active,
model.Polling,
(d, obj) => synchronizationContext.Post(d, obj),
this);
}
catch (Exception exception)
{
Log.ForContext<MyBikesPage>().Error("Displaying bikes at station page failed. {Exception}", exception);
this.DisplayAlert("Fehler", $"Seite Räder an Station kann nicht angezeigt werden. ${exception.Message}", "OK");
return;
}
InitializeComponent();
BindingContext = m_oViewModel;
MyBikesListView.ItemsSource = m_oViewModel;
await m_oViewModel.OnAppearing();
}
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
protected async override void OnDisappearing()
{
if (m_oViewModel == null)
{
// View model might be null (Example: Occured when page to querry for location permissions was opened)
return;
}
await m_oViewModel.OnDisappearing();
}
/// <summary>
/// Displays altert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
{
await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public 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 alert message.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
{
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotSupportedException();
}
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
throw new NotSupportedException();
}
/// <summary> Pops a page from the modal stack. </summary>
public Task PopModalAsync()
{
throw new NotSupportedException();
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushAsync(ViewTypes p_oTypeOfPage)
{
throw new NotSupportedException();
}
public async Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
}
}

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<FlyoutPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.Root.RootPage"
xmlns:pages="clr-namespace:TINK.View.Root"
xmlns:mappage="clr-namespace:TINK.View.Map">
<FlyoutPage.Flyout>
<pages:RootPageFlyout x:Name="FlyoutPage" />
</FlyoutPage.Flyout>
<FlyoutPage.Detail>
<NavigationPage>
<x:Arguments>
<mappage:MapPage />
</x:Arguments>
</NavigationPage>
</FlyoutPage.Detail>
</FlyoutPage>

View file

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TINK.View.MasterDetail;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Root
{
/// <summary>
/// Mamages creation of detail pages if a flyout page menu entry is selected.
/// Exposes flyout page style navigation which is used by detail pages.
/// </summary>
/// <remarks>
/// Examples of use cases when detail pages do navigation:
// - switch to map page after succesfully logging in/ logging out
// - switch to login page form bikes at station page if not yet logged in
/// </remarks>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RootPage : FlyoutPage, INavigationMasterDetail
{
public RootPage()
{
InitializeComponent();
FlyoutPage.ListView.ItemSelected += OnListViewItemSelected;
// Any type of split behaviour conflics with map shifting functionality (assuming FlyoutPage behaves same like MasterDetailPage).
FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover;
var navigationPage = Detail as NavigationPage;
if (navigationPage == null)
{
return;
}
var detailPage = navigationPage.RootPage as IDetailPage;
if (detailPage == null)
{
return;
}
detailPage.NavigationMasterDetail = this;
}
/// <summary>
/// Is called if a flyout page menu entry is selected.
/// Creates a new page.
/// </summary>
private void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as RootPageFlyoutMenuItem;
if (item == null)
return;
ShowPage(item.TargetType, item.Title);
IsPresented = false;
FlyoutPage.ListView.SelectedItem = null;
}
/// <summary>
/// Shows a detail page.
/// </summary>
/// <param name="typeOfPage">Type of page to show.</param>
/// <param name="title">Title of page.</param>
public void ShowPage(Type typeOfPage, string title)
{
var page = (Page)Activator.CreateInstance(typeOfPage);
page.Title = title;
if (page is IDetailPage detailPage)
{
// Detail page needs reference to perform navigation.
// Examples see above in xdoc of class.
detailPage.NavigationMasterDetail = this;
}
Detail = new NavigationPage(page);
}
}
}

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.Root.RootPageFlyout"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
xmlns:local="clr-namespace:TINK.View"
IconImageSource="Icon-Small@2x"
Title="sharee.bike">
<StackLayout>
<ListView x:Name="MenuItemsListView"
SeparatorVisibility="None"
HasUnevenRows="true"
local:ListViewAttachedBehavior.Command="{Binding MenuItemSelected}"
ItemsSource="{Binding MenuItems}">
<ListView.Header>
<Grid BackgroundColor="{DynamicResource Key=primary-back-title-color}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="80"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Label
Grid.Column="1"
Grid.Row="2"
Text="{Binding MasterDetailMenuTitlte}"
Style="{DynamicResource SubtitleStyle}"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image>
<Image.Source>
<FontImageSource Glyph="{Binding GlyphCode}" Color="Black" FontFamily="FA-S"/>
</Image.Source>
</Image>
<Label
Grid.Column="1"
Margin="10, 0, 0, 0"
VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center"
FontSize="18"
Text="{Binding Title}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>

View file

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using TINK.Model;
using TINK.Services;
using TINK.View.MasterDetail;
using TINK.View.Themes;
using TINK.ViewModel.Root;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.Root
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RootPageFlyout : ContentPage
{
public ListView ListView;
public RootPageFlyout()
{
InitializeComponent();
BindingContext = new RootPageFlyoutViewModel();
ListView = MenuItemsListView;
}
}
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TINK.View.Root
{
public class RootPageFlyoutMenuItem
{
public RootPageFlyoutMenuItem()
{
TargetType = typeof(RootPageFlyoutMenuItem);
}
public int Id { get; set; }
public string GlyphCode { get; set; }
public string Title { get; set; }
public Type TargetType { get; set; }
}
}

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.MainPage"
xmlns:pages="clr-namespace:TINK.View"
xmlns:mappage="clr-namespace:TINK.View.Map">
<MasterDetailPage.Master>
<pages:MasterPage x:Name="MasterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<mappage:MapPage />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>

View file

@ -0,0 +1,81 @@
using System;
using TINK.View.Info.BikeInfo;
using TINK.View.MasterDetail;
using TINK.ViewModel.MasterDetail;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View
{
/// <summary>
/// Delegate to perform navigation.
/// </summary>
public delegate void ShowPageDelegate(Type p_oType, string p_strTitle = null);
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : MasterDetailPage, INavigationMasterDetail
{
public MainPage()
{
InitializeComponent();
MasterPage.ShowPageViewService = ShowPage;
// Any type of split behaviour conflics with map shifting functionality.
MasterBehavior = MasterBehavior.Popover;
var navigationPage = Detail as NavigationPage;
if (navigationPage == null)
{
return;
}
var detailPage = navigationPage.RootPage as IDetailPage;
if (detailPage == null)
{
return;
}
detailPage.NavigationMasterDetail = this;
}
/// <summary> Creates and a page an shows it.</summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(
Type p_oTypeOfPage,
string p_strTitle = null)
{
if (p_oTypeOfPage == null)
return;
var page = (Page)Activator.CreateInstance(p_oTypeOfPage);
page.Title = p_strTitle ?? Helper.GetCaption(p_oTypeOfPage);
var l_oPage = page as IDetailPage;
if (l_oPage != null)
{
l_oPage.NavigationMasterDetail = this;
}
#if !BACKSTYLE
// When bike info page is shown do not allow to close this carousel before all pages were displayed.
IsGestureEnabled = p_oTypeOfPage != typeof(BikeInfoCarouselPage);
Detail = new NavigationPage(page);
IsPresented = false;
#else
Detail.Navigation.PushAsync(page);
IsPresented = false;
while (Detail.Navigation.NavigationStack.Count > 2)
{
// Ensure that stack does never contains more than 2 pages (root page + one child)
// This can occure if from child via swipe onther child is opened.
// Remove item after adding new one to avoid flickering.
Detail.Navigation.RemovePage(Detail.Navigation.NavigationStack[1]);
}
#endif
MasterPage.ListView.SelectedItem = null;
}
}
}

View file

@ -0,0 +1,42 @@
using System;
using TINK.View.Map;
using TINK.ViewModel.MasterDetail;
namespace TINK.View
{
public class MainPageMenuItem
{
public MainPageMenuItem()
{
TargetType = typeof(MapPage);
}
public MainPageMenuItem(
int p_iId,
Type p_oTypeOfPage,
string p_strTitle = null)
{
TargetType = p_oTypeOfPage;
Id = p_iId;
Title = p_strTitle ?? Helper.GetCaption(p_oTypeOfPage);
}
public int Id
{
get;
private set;
}
public string Title
{
get;
private set;
}
public Type TargetType
{
get;
private set;
}
}
}

View file

@ -0,0 +1,51 @@
<?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:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
x:Class="TINK.View.MasterPage"
xmlns:local="clr-namespace:TINK.View"
Icon="Icon-Small@2x"
Title="TINK">
<StackLayout>
<ListView x:Name="MenuItemsListView"
SeparatorVisibility="None"
HasUnevenRows="true"
local:ListViewAttachedBehavior.Command="{Binding MenuItemSelected}"
SelectedItem="{Binding SelectedMenuItem}"
ItemsSource="{Binding MenuItems}">
<ListView.Header>
<Grid BackgroundColor="{DynamicResource Key=primary-back-title-color}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="80"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Label
Grid.Column="1"
Grid.Row="2"
FontSize="Large"
Text="{Binding MasterDetailMenuTitlte}"
Style="{DynamicResource SubtitleStyle}"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="15,10" HorizontalOptions="FillAndExpand">
<Label VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center"
Text="{Binding Title}"
FontSize="24"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>

View file

@ -0,0 +1,32 @@
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MasterPage : ContentPage
{
public ListView ListView;
/// <summary>
/// Master page view model
/// </summary>
private MasterPageViewModel m_oMasterPageViewModel;
/// <summary> Delegate to perform navigation between detail pages. </summary>
public ShowPageDelegate ShowPageViewService { set => m_oMasterPageViewModel.ShowPageViewService = value; }
public MasterPage()
{
InitializeComponent();
m_oMasterPageViewModel = new MasterPageViewModel();
BindingContext = m_oMasterPageViewModel;
ListView = MenuItemsListView;
}
}
}

View file

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
xmlns:mappage="clr-namespace:TINK.View.Map"
xmlns:mybikes="clr-namespace:TINK.View.MyBikes"
xmlns:account="clr-namespace:TINK.View.Account"
xmlns:login="clr-namespace:TINK.View.Login"
xmlns:settings="clr-namespace:TINK.View.Settings"
xmlns:contact="clr-namespace:TINK.View.Contact"
xmlns:info="clr-namespace:TINK.View.Info"
xmlns:header="clr-namespace:TINK.View.RootShell"
BackgroundColor="{DynamicResource Key=primary-back-title-color}"
Title="Shell"
x:Class="TINK.View.RootShell.AppShell">
<Shell.FlyoutHeader>
<header:FlyoutHeader/>
</Shell.FlyoutHeader>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingMapPage}"
ContentTemplate="{DataTemplate mappage:MapPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconMap}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingMyBikes}"
IsVisible="{Binding IsMyBikesPageVisible}"
ContentTemplate="{DataTemplate mybikes:MyBikesPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconMyBikes}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingAccount}"
IsVisible="{Binding IsAccountPageVisible}"
ContentTemplate="{DataTemplate account:AccountPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconAccount}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingLogin}"
IsVisible="{Binding IsLoginPageVisible}"
ContentTemplate="{DataTemplate login:LoginPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconLogin}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingSettings}"
IsVisible="{Binding IsSettingsPageVisible}"
ContentTemplate="{DataTemplate settings:SettingsPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconSettings}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingFeesAndBikes}"
ContentTemplate="{DataTemplate contact:FeesAndBikesPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconFeesAndBikes}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{x:Static resources:AppResources.MarkingFeedbackAndContact}"
ContentTemplate="{DataTemplate contact:ContactPage}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconContact}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent
Title="{Binding TabbedPageIngoTitle}"
ContentTemplate="{DataTemplate info:TabbedPageInfo}">
<ShellContent.FlyoutIcon>
<FontImageSource Glyph="{StaticResource IconInfo}" Color="Black" FontFamily="FA-S" />
</ShellContent.FlyoutIcon>
</ShellContent>
</FlyoutItem>
</Shell>

View file

@ -0,0 +1,17 @@

using System;
using System.Collections.Generic;
using TINK.ViewModel.RootShell;
using Xamarin.Forms;
namespace TINK.View.RootShell
{
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
BindingContext = new AppShellViewModel();
}
}
}

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
BackgroundColor="{DynamicResource Key=primary-back-title-color}"
x:Class="TINK.View.RootShell.FlyoutHeader">
<Grid Padding="5,10">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Image
HeightRequest="140"
Aspect="AspectFit"
Source="sharee_no_background.png" />
<Label
HorizontalOptions="CenterAndExpand"
FontSize="Large"
Grid.Row="1"
Text="{Binding MasterDetailMenuTitlte}"/>
</Grid>
</ContentView>

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.RootShell
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FlyoutHeader : ContentView
{
public FlyoutHeader ()
{
InitializeComponent ();
}
}
}

View file

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Forms;
using TINK.Model.User.Account;
using System.Linq;
using System.Reflection;
namespace TINK.View.Settings
{
/// <summary>
/// Translates user permissions into visibility state.
/// Used for container which holds a bunch of GUI elemets which migth all/ partly/ none be visible
/// If all childs are invisible frame must be invisible as well. As soon as one child is visible frame must be visible as well.
/// </summary>
public class AnyPermissionToVisibleConverter : IValueConverter
{
/// <summary> Converts permission value into visible state.</summary>
/// <param name="value">Permission value from view model used to derive whether object is visible or not.</param>
/// <returns>Boolean value indicating whether object is visible or not.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Permissions)(value)) != Permissions.None;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Permissions.None;
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Forms;
using TINK.Model.User.Account;
using System.Linq;
using System.Reflection;
namespace TINK.View.Settings
{
/// <summary> Translates user permissions into visibility state. </summary>
public class PermissionToVisibleConverter : BindableObject, IValueConverter
{
static readonly BindableProperty VisibleFlagProperty =
BindableProperty.Create(nameof(VisibleFlag), typeof(Permissions), typeof(BindableObject));
/// <summary> Property set from XAML determinig for which permission value object is visible.</summary>
public Permissions VisibleFlag
{
get => (Permissions)GetValue(VisibleFlagProperty);
set => SetValue(VisibleFlagProperty, value);
}
/// <summary> Converts permission value into visible state.</summary>
/// <param name="value">Permission value from view model used to derive whether object is visible or not.</param>
/// <returns>Boolean value indicating whether object is visible or not.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Permissions)(value)).HasFlag(VisibleFlag);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Permissions.None;
}
}
}

View file

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.Settings.SettingsPage"
xmlns:conv="clr-namespace:TINK.View.Settings">
<ContentPage.Resources>
<conv:AnyPermissionToVisibleConverter x:Key="Frame_Converter"/>
<conv:PermissionToVisibleConverter x:Key="PickCopriServer_Converter" VisibleFlag="PickCopriServer"/>
<conv:PermissionToVisibleConverter x:Key="ManagePolling_Converter" VisibleFlag="ManagePolling"/>
<conv:PermissionToVisibleConverter x:Key="ManageCopriCacheExpiration_Converter" VisibleFlag="ManageCopriCacheExpiration"/>
<conv:PermissionToVisibleConverter x:Key="PickLockServiceImplementation_Converter" VisibleFlag="PickLockServiceImplementation"/>
<conv:PermissionToVisibleConverter x:Key="PickLocationServiceImplementation_Converter" VisibleFlag="PickLocationServiceImplementation"/>
<conv:PermissionToVisibleConverter x:Key="PickLoggingLevel_Converter" VisibleFlag="PickLoggingLevel"/>
<conv:PermissionToVisibleConverter x:Key="ShowDiagnostics_Converter" VisibleFlag="ShowDiagnostics"/>
<conv:PermissionToVisibleConverter x:Key="SwitchSiteCaching_Converter" VisibleFlag="SwitchNoSiteCaching"/>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<Frame>
<StackLayout>
<!--
<Button Text="Feedback" Clicked="OnFeedbackClickedAsync"/>
-->
<Frame>
<StackLayout>
<Label Text="Karte auf aktuelle Position ausrichten"/>
<Switch IsToggled="{Binding CenterMapToCurrentLocation}"/>
</StackLayout>
</Frame>
<!-- Filter on view TINK/ Konrad -->
<Frame IsVisible="{Binding IsGroupFilterVisible}">
<StackLayout>
<Label Text="Ausblenden/ Einblenden"/>
<ListView
HasUnevenRows="True"
HeightRequest="120"
x:Name="Filters">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="{Binding IsEnabled}">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Text}"/>
<Switch IsToggled="{Binding IsActivated}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<StackLayout>
<!-- Themes -->
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickCopriServer_Converter}}"
Text="Theme"/>
<Picker
IsVisible="{Binding DebugLevel, Converter={StaticResource PickCopriServer_Converter}}"
ItemsSource="{Binding Themes.ServicesTextList}"
SelectedItem="{Binding Themes.ActiveText}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- COPRI server selection -->
<StackLayout>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickCopriServer_Converter}}"
Text="{Binding CopriServerUriList.CorpiServerUriDescription}"/>
<Picker
IsVisible="{Binding DebugLevel, Converter={StaticResource PickCopriServer_Converter}}"
ItemsSource="{Binding CopriServerUriList.ServerTextList}"
SelectedItem="{Binding CopriServerUriList.NextActiveServerText}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ManagePolling_Converter}}"
Text="{Binding Polling.PollingText}"/>
<Switch
IsVisible="{Binding DebugLevel, Converter={StaticResource ManagePolling_Converter}}"
IsToggled="{Binding Polling.IsActivated}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ManagePolling_Converter}}"
Text="{Binding Polling.PeriodeTotalSecondsText}"/>
<Stepper
IsVisible="{Binding DebugLevel, Converter={StaticResource ManagePolling_Converter}}"
Minimum="5"
Increment="5"
Maximum="600"
IsEnabled="{Binding Polling.IsActivated}"
Value="{Binding Polling.PeriodeTotalSeconds}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ManageCopriCacheExpiration_Converter}}"
Text="Time after which COPRI-cache expires [s]"/>
<Slider
IsVisible="{Binding DebugLevel, Converter={StaticResource ManageCopriCacheExpiration_Converter}}"
x:Name="expiresAfter"
Minimum="0"
Maximum="15"
Value="{Binding ExpiresAfterTotalSeconds}"/>
<Entry
IsVisible="{Binding DebugLevel, Converter={StaticResource ManageCopriCacheExpiration_Converter}}"
IsReadOnly="True"
Text="{Binding ExpiresAfterTotalSecondsText}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- Lock control -->
<StackLayout>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLockServiceImplementation_Converter}}"
Text="Lock Control" />
<Picker
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLockServiceImplementation_Converter}}"
ItemsSource="{Binding LocksServices.Services.ServicesTextList}"
SelectedItem="{Binding LocksServices.Services.ActiveText}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLockServiceImplementation_Converter}}"
Text="Bluetooth Connect Timeout [sec]"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLockServiceImplementation_Converter}}"
Text="{Binding LocksServices.ConnectTimeoutSecText}"/>
<Stepper
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLockServiceImplementation_Converter}}"
Minimum="0.1"
Increment="0.25"
Maximum="60"
Value="{Binding LocksServices.ConnectTimeoutSec}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- Geolocation -->
<StackLayout>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLocationServiceImplementation_Converter}}"
Text="Geolocation Control" />
<Picker
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLocationServiceImplementation_Converter}}"
ItemsSource="{Binding GeolocationServices.ServicesTextList}"
SelectedItem="{Binding GeolocationServices.ActiveText}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- Web site caching -->
<StackLayout>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource SwitchSiteCaching_Converter}}"
Text="Caching von Websiten."/>
<Switch
IsVisible="{Binding DebugLevel, Converter={StaticResource SwitchSiteCaching_Converter}}"
IsToggled="{Binding IsSiteCachingOnDisplayValue}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- Logging -->
<StackLayout>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLoggingLevel_Converter}}"
Text="Logging level" />
<Picker
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLoggingLevel_Converter}}"
ItemsSource="{Binding LoggingLevels}"
SelectedItem="{Binding SelectedLoggingLevel}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLoggingLevel_Converter}}"
Text="Logdatei in externen Pfad schreiben"/>
<Switch
IsVisible="{Binding DebugLevel, Converter={StaticResource PickLoggingLevel_Converter}}"
IsToggled="{Binding LogToExternalFolderDisplayValue}"
IsEnabled="{Binding IsLogToExternalFolderVisible}"/>
</StackLayout>
</Frame>
<Frame
IsVisible="{Binding DebugLevel, Converter={StaticResource Frame_Converter}}">
<!-- Display of parameters -->
<StackLayout>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
Text="Device Identifier" />
<Entry
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
IsEnabled="false"
Text="{Binding DeviceIdentifier}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
Text="Copri Sitzungkeks"/>
<Entry
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
IsEnabled="false"
Text="{Binding SessionCookie}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
Text="Interner Pfad (Einstell./ ggf. Logging)"/>
<Editor
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
IsEnabled="false"
Text="{Binding InternalPath}"/>
<Label
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
Text="Externer Pfad (Mock/ ggf. Logging)"/>
<Editor
IsVisible="{Binding DebugLevel, Converter={StaticResource ShowDiagnostics_Converter}}"
IsEnabled="false"
Text="{Binding ExternalPath}"/>
</StackLayout>
</Frame>
</StackLayout>
</Frame>
</ScrollView>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,135 @@
using TINK.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Threading.Tasks;
using TINK.View.MasterDetail;
using System;
using TINK.Model.Device;
using Xamarin.CommunityToolkit.Extensions;
namespace TINK.View.Settings
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SettingsPage : ContentPage, IViewService, IDetailPage
{
/// <summary> Refernce to view model. </summary>
SettingsPageViewModel m_oViewModel = null;
/// <summary> Constructs a settings page. </summary>
public SettingsPage ()
{
InitializeComponent ();
var l_oModel = App.ModelRoot;
m_oViewModel = new SettingsPageViewModel(
l_oModel,
this);
BindingContext = m_oViewModel;
Filters.ItemsSource = m_oViewModel.GroupFilter;
}
/// <summary> Displays alert message. </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Type of buttons.</param>
public new async Task DisplayAlert(string p_strTitle, string p_strMessage, string p_strCancel)
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strCancel);
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Displays alert message.</summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strAccept">Text of accept button.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <returns>True if user pressed accept.</returns>
public new async Task<bool> DisplayAlert(string p_strTitle, string p_strMessage, string p_strAccept, string p_strCancel)
=> await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
/// <summary>
/// Displays an action sheet.
/// </summary>
/// <param name="p_strTitle">Title of message.</param>
/// <param name="p_strMessage">Message to display.</param>
/// <param name="p_strCancel">Text of button.</param>
/// <param name="destruction"></param>
/// <param name="p_oButtons">Buttons holding options to select.</param>
/// <returns>Text selected</returns>
public new async Task<string> DisplayActionSheet(String p_strTitle, String p_strCancel, String destruction, params String[] p_oButtons)
=> await base.DisplayActionSheet(p_strTitle, p_strCancel, destruction, p_oButtons);
/// <summary>
/// Creates and a page an shows it.
/// </summary>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> m_oNavigation.ShowPage(p_oType.GetViewType(), p_strTitle);
/// <summary> Pushes a page onto the modal stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
=> throw new NotImplementedException();
/// <summary> Pops a page from the modal stack. </summary>
public Task PopModalAsync()
=> throw new NotSupportedException();
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigation;
/// <summary>
/// Delegate to perform navigation.
/// </summary>
public INavigationMasterDetail NavigationMasterDetail
{
set { m_oNavigation = value; }
}
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
protected async override void OnDisappearing()
{
if (m_oViewModel == null)
{
// View model might be null.
return;
}
await m_oViewModel?.OnDisappearing();
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes p_oTypeOfPage)
=> await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
public async Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => await Navigation.ShowPopupAsync<FeedbackPopup.Result>(new FeedbackPopup());
#if USERFEEDBACKDLG_TRYOUT
public async void OnFeedbackClickedAsync(object sender, EventArgs ev)
{
var result = await DisplayUserFeedbackPopup();
DisplayAlert(
"Title",
$"Bike broken: {result.IsBikeBroken}. Message: {result.Message}.",
"OK");
}
#endif
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Forms;
namespace TINK.View
{
/// <summary> Converts a string into visible state. If string is null or empty element becomes invisible.</summary>
public class StringNotNullOrEmptyToVisibleConverter : IValueConverter
{
/// <summary> Converts a string into visible state.</summary>
/// <param name="value">Text value from view model used to derive whether object is visible or not.</param>
/// <returns>Boolean value indicating whether object is visible or not.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null && value is string text && !string.IsNullOrEmpty(text));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return "";
}
}
}

View file

@ -0,0 +1,67 @@
using System;
using TINK.View.Contact;
using TINK.View.CopriWebView;
using TINK.View.Info;
using TINK.View.Info.BikeInfo;
using TINK.View.Login;
using TINK.View.Map;
using TINK.View.MyBikes;
using TINK.View.Settings;
using TINK.View.WhatsNew;
using TINK.View.WhatsNew.Agb;
using TINK.View.BikesAtStation;
using Xamarin.Forms;
namespace TINK.View
{
public static class ViewTypesTypeProvider
{
public static Type GetViewType(this ViewTypes viewType)
{
switch (viewType)
{
case ViewTypes.LoginPage:
return typeof(LoginPage);
case ViewTypes.MapPage:
return typeof(MapPage);
case ViewTypes.RegisterPage:
return typeof(RegisterPage);
case ViewTypes.PasswordForgottenPage:
return typeof(PasswordForgottenPage);
case ViewTypes.BikeInfoCarouselPage:
return typeof(BikeInfoCarouselPage);
case ViewTypes.MyBikesPage:
return typeof(MyBikesPage);
case ViewTypes.SettingsPage:
return typeof(SettingsPage);
case ViewTypes.TabbedPageInfo:
return typeof(TabbedPageInfo);
case ViewTypes.TabbedPageHelpContact:
return typeof(FeesAndBikesPage);
case ViewTypes.ManageAccountPage:
return typeof(ManageAccountPage);
case ViewTypes.AgbPage:
return typeof(AgbPage);
case ViewTypes.WhatsNewPage:
return typeof(WhatsNewPage);
case ViewTypes.BikesAtStation:
return typeof(BikesAtStationPage);
default:
return typeof(ContentPage);
}
}
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.WhatsNew.Agb.AgbPage">
<ContentPage.Content>
<Frame>
<StackLayout>
<WebView
HeightRequest="1000"
WidthRequest="500"
Source="{Binding InfoAgb}"/>
<Button Text="OK"
Command="{Binding OnOk}"/>
</StackLayout>
</Frame>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,71 @@
using System;
using System.Threading.Tasks;
using TINK.ViewModel;
using TINK.ViewModel.WhatsNew.Agb;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.WhatsNew.Agb
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AgbPage : ContentPage, IViewService
{
public AgbPage ()
{
InitializeComponent ();
agbViewModel = new AgbViewModel(
App.ModelRoot.NextActiveUri.Host,
App.ModelRoot.IsSiteCachingOn,
(resourceName) => ViewModelResourceHelper.GetSource(resourceName),
this);
BindingContext = agbViewModel;
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public new async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
/// <summary> Invoked when page is shown. </summary>
protected async override void OnAppearing()
{
await agbViewModel.OnAppearing();
}
/// <summary> Reference to view model.</summary>
AgbViewModel agbViewModel;
public async Task PopModalAsync()
{
await Navigation.PopModalAsync();
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushAsync(ViewTypes p_oTypeOfPage)
{
throw new NotImplementedException();
}
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
throw new NotImplementedException();
}
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotImplementedException();
}
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
}
}

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.WhatsNew.WhatsNewPage">
<ContentPage.Content>
<ScrollView>
<Frame>
<StackLayout>
<Label Text="{Binding WhatsNewTitle}"
FontSize="Large"
VerticalOptions="Start"
HorizontalOptions="CenterAndExpand" />
<Frame>
<StackLayout>
<Label
Text="{Binding WhatsNewText}"
TextType="Html"/>
<Label FormattedText="{Binding AgbChangedText}"
IsVisible="{Binding IsAgbChangedVisible}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnShowAgbTapped}"/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</Frame>
<Frame IsVisible="{Binding IsFeedbackVisible}">
<StackLayout>
<Label Text="Gibts Verbesserungsvorschläge/ Probleme mit der App?"/>
<Button Text="Verbesserungsmail schreiben"/>
<Label Text="Die App läuft rund und tut was sie soll? Über eine Bewertung würden wir uns sehr freuen!"/>
<Button Text="Bewertung abgeben"/>
</StackLayout>
</Frame>
<Button
Text="OK"
Command="{Binding OnOk}"/>
</StackLayout>
</Frame>
</ScrollView>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,87 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Device;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View.WhatsNew
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class WhatsNewPage : ContentPage, IViewService
{
/// <summary> Holds a reference on the view model. </summary>
private ViewModel.WhatsNew.WhatsNewViewModel WhatsNewViewModel;
/// <summary> Constructs whats new page.</summary>
/// <param name="showMasterDetail">Action to invoke master detail page.</param>
public WhatsNewPage(Action showMasterDetail)
{
InitializeComponent();
WhatsNewViewModel = new ViewModel.WhatsNew.WhatsNewViewModel(
DependencyService.Get<IAppInfo>().Version,
App.ModelRoot.WhatsNew.WhatsNewText,
App.ModelRoot.WhatsNew.IsShowAgbRequired,
showMasterDetail,
this);
BindingContext = WhatsNewViewModel;
}
/// <summary> Displays altert message.</summary>
/// <param name="title">Title of message.</param>
/// <param name="message">Message to display.</param>
/// <param name="details">Detailed error description.</param>
/// <param name="cancel">Type of buttons.</param>
public async Task DisplayAdvancedAlert(
string title,
string message,
string details,
string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", cancel);
public Task PopModalAsync()
{
throw new NotImplementedException(); ;
}
/// <summary> Pushes a page onto the stack. </summary>
/// <param name="p_oTypeOfPage">Page to display.</param>
public Task PushAsync(ViewTypes p_oTypeOfPage)
{
throw new NotImplementedException();
}
public async Task PushModalAsync(ViewTypes p_oTypeOfPage)
{
await Navigation.PushModalAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
}
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
{
throw new NotImplementedException();
}
public Task<IViewService.IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
/// <summary>
/// Invoked when pages is closed/ hidden.
/// /// </summary>
protected override void OnDisappearing()
{
base.OnDisappearing();
if (WhatsNewViewModel == null)
{
// View model might be null.
return;
}
WhatsNewViewModel?.OnDisappearing(() =>
{
App.ModelRoot.SetWhatsNewWasShown();
App.ModelRoot.Save();
});
}
}
}