Closing lock and returning bike speeded up.

This commit is contained in:
Oliver Hauff 2021-08-28 10:04:10 +02:00
parent e4adeb908c
commit db9c288584
70 changed files with 933 additions and 902 deletions

View file

@ -18,6 +18,6 @@ namespace TINK.Droid.Model.Device
/// <summary> Gets unitque device identifier. </summary>
/// <returns>Gets the identifies specifying device.</returns>
public string Identifier
=> Android.Provider.Settings.Secure.GetString(Forms.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
=> Android.Provider.Settings.Secure.GetString(Android.App.Application.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
}
}

View file

@ -22,7 +22,7 @@ namespace TINK.Droid.Model.Device
{
get
{
LocationManager locationManager = (LocationManager)Forms.Context.GetSystemService(Context.LocationService);
LocationManager locationManager = (LocationManager)Android.App.Application.Context.GetSystemService(Context.LocationService);
return locationManager.IsProviderEnabled(LocationManager.GpsProvider);
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.243" android:versionCode="243">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.244" android:versionCode="244">
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="30" />
<!-- Google Maps related permissions -->
<permission android:name="com.ecs.google.maps.v2.actionbarsherlock.permission.MAPS_RECEIVE" android:protectionLevel="signature" />

View file

@ -32,25 +32,27 @@
<string>TINK</string>
<key>XSAppIconAssets</key>
<string>Media.xcassets/AppIcons.appiconset</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Communicate with Bluetooth lock.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Communicate with Bluetooth lock.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Show map at current positon and pass positon to server when returning bike.</string>
<!--Description of the Bluetooth request message (required on iOS 13)-->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Is required to communicate with Bluetooth lock.</string>
<!--Description of the Bluetooth request message (required on iOS 10, deprecated)-->
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Is required to communicate with Bluetooth lock.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Is required to show map at current position and pass position to server when returning bike.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Show map at current positon and pass positon to server when returning bike.</string>
<string>Is required to show map at current position and pass position to server when returning bike.</string>
<key>CFBundleIdentifier</key>
<string>com.TeilRad.sharee.bike</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show map at current positon and pass positon to server when returning bike.</string>
<string>Is required to show map at current position and pass position to server when returning bike.</string>
<key>CFBundleDisplayName</key>
<string>sharee.bike</string>
<key>CFBundleVersion</key>
<string>243</string>
<string>244</string>
<key>CFBundleShortVersionString</key>
<string>3.0.241</string>
<string>3.0.244</string>
</dict>
</plist>

View file

@ -127,21 +127,7 @@ namespace TINK
if (lastVersion > new Version(3, 0, 173))
GeolocationServicesContainer.SetActive(settings.ActiveGeolocationService);
if (new Version(0, 0, 0) < lastVersion
&& lastVersion <= new Version(3, 0, 234))
{
// Version 3.0.245 and older used Xamarin.Auth.AccountStore to securely store data.
// Later version s use Xamarin.Essentials Secure Storage.
store = new StoreLegacy(settings.ActiveUri.GetHashCode().ToString());
}
else
{
// Either
// - frist install or
// - version whitch uses secure storage
// detected.
store = new Store();
}
store = new Store();
Barrel.ApplicationId = "TINKApp";
@ -184,12 +170,7 @@ namespace TINK
{
InitializeComponent();
#if USEMASTERDETAIL
// Use master detail page.
MainPage = ModelRoot.WhatsNew.IsShowRequired
? new View.WhatsNew.WhatsNewPage(() => MainPage = new View.MainPage()) // Show whats new info.
: (Page) new View.MainPage(); // Just use TINKApp
#elif USEFLYOUT
#if USEFLYOUT
// Use flyout page.
MainPage = ModelRoot.WhatsNew.IsShowRequired
? new View.WhatsNew.WhatsNewPage(() => MainPage = new View.Root.RootPage()) // Show whats new info.

View file

@ -26,7 +26,12 @@ namespace TINK
/// <returns></returns>
static Page GetCurrentPage()
{
return (Application.Current.MainPage as MasterDetailPage)?.Detail.Navigation.NavigationStack.LastOrDefault();
#if USEFLYOUT
return (Application.Current.MainPage as FlyoutPage)?.Detail.Navigation.NavigationStack.LastOrDefault();
#else
return (Application.Current.MainPage as AppShellViewModel)?.Detail.Navigation.NavigationStack.LastOrDefault();
#endif
}
}

View file

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

View file

@ -99,10 +99,8 @@
<DependentUpon>WhatsNewPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Model\User\Account\Store.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\ViewModelResourceHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\RootMasterDetail\Helper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModel\RootMasterDetail\MasterPageViewModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)View\BikesAtStation\BikesAtStationPage.xaml.cs">
<DependentUpon>BikesAtStationPage.xaml</DependentUpon>
<SubType>Code</SubType>
@ -123,14 +121,6 @@
<DependentUpon>LoginPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MasterPage.xaml.cs">
<DependentUpon>MasterPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MainPageMenuItem.cs">
<SubType>Code</SubType>
</Compile>
@ -181,18 +171,6 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MainPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)View\RootMasterDetail\MasterPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)View\Contact\ContactPage.xaml">
<SubType>Designer</SubType>

View file

@ -2,7 +2,7 @@
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using System;
@ -12,7 +12,7 @@ using TINK.ViewModel.Account;
namespace TINK.View.Account
{
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class AccountPage : ContentPage, IViewService, IDetailPage
#else
public partial class AccountPage : ContentPage, IViewService
@ -86,7 +86,7 @@ namespace TINK.View.Account
public new async Task<string> DisplayActionSheet(String title, String cancel, String destruction, params String[] p_oButtons)
=> await base.DisplayActionSheet(title, cancel, destruction, p_oButtons);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
@ -108,7 +108,7 @@ namespace TINK.View.Account
public Task PopModalAsync()
=> throw new NotSupportedException();
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigation;

View file

@ -10,7 +10,7 @@ namespace TINK.View.BikesAtStation
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Device;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel;
@ -25,7 +25,7 @@ using TINK.View.MasterDetail;
using Xamarin.CommunityToolkit.Extensions;
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class BikesAtStationPage : ContentPage, IViewService, IDetailPage
#else
public partial class BikesAtStationPage : ContentPage, IViewService
@ -180,7 +180,7 @@ using TINK.View.MasterDetail;
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary> Creates and a page an shows it.</summary>
/// <remarks> When user is not logged in navigation to Login page is supported.</remarks>
/// <param name="p_oTypeOfPage">Type of page to show.</param>
@ -209,7 +209,7 @@ using TINK.View.MasterDetail;
throw new NotImplementedException();
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Delegate to perform navigation.
/// </summary>

View file

@ -47,8 +47,8 @@ namespace TINK.View.Contact
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
public void ShowPage(ViewTypes p_oType, string title = null)
=> NavigationMasterDetail.ShowPage(p_oType.GetViewType(), title);
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> throw new NotImplementedException();
#else
/// <summary> Shows a page.</summary>
/// <param name="route">Route of the page to show.</param>
@ -91,7 +91,7 @@ namespace TINK.View.Contact
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Delegate to perform navigation.

View file

@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using Xamarin.Forms;
@ -14,10 +14,10 @@ namespace TINK.View.Contact
using TINK.ViewModel.Contact;
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class SelectStationPage : ContentPage, IViewService, IDetailPage
#else
public partial class MapPage : ContentPage, IViewService
public partial class SelectStationPage : ContentPage, IViewService
#endif
{
/// <summary> View model to notify about whether page appears or hides. </summary>
@ -70,7 +70,7 @@ namespace TINK.View.Contact
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
@ -96,17 +96,17 @@ namespace TINK.View.Contact
/// <param name="typeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes typeOfPage)
{
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
var page = Activator.CreateInstance(typeOfPage.GetViewType()) as IDetailPage;
#else
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
var page = Activator.CreateInstance(typeOfPage.GetViewType());
#endif
if (page == null)
{
return;
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
page.NavigationMasterDetail = NavigationMasterDetail;
#endif
@ -119,7 +119,7 @@ namespace TINK.View.Contact
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary> Delegate to perform navigation.</summary>
public INavigationMasterDetail NavigationMasterDetail { private get; set; }
@ -159,7 +159,7 @@ namespace TINK.View.Contact
{
BindingContext = SelectStationPageViewModel;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
SelectStationPageViewModel.NavigationMasterDetail = NavigationMasterDetail;
#endif
}

View file

@ -114,7 +114,7 @@ namespace TINK.View.FindBike
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);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> throw new NotImplementedException();
#else

View file

@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel;
@ -11,7 +11,7 @@ using Xamarin.Forms.Xaml;
namespace TINK.View.Info.BikeInfo
{
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class BikeInfoCarouselPage : CarouselPage, IViewService, IDetailPage
#else
public partial class BikeInfoCarouselPage : CarouselPage, IViewService
@ -70,7 +70,7 @@ namespace TINK.View.Info.BikeInfo
return await App.Current.MainPage.DisplayAlert(p_strTitle, p_strMessage, p_strAccept, p_strCancel);
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
@ -104,7 +104,7 @@ namespace TINK.View.Info.BikeInfo
throw new NotImplementedException();
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Delegate to perform navigation.
/// </summary>

View file

@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Device;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel;
@ -11,7 +11,7 @@ using Xamarin.Forms.Xaml;
namespace TINK.View.Login
{
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class LoginPage : ContentPage, IViewService, IDetailPage
#else
public partial class LoginPage : ContentPage, IViewService
@ -65,7 +65,7 @@ namespace TINK.View.Login
public async Task<bool> DisplayAdvancedAlert(string title, string message, string details, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, $"{message}\r\nDetails:\r\n{details}", accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
@ -98,7 +98,7 @@ namespace TINK.View.Login
await Navigation.PushAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Delegate to perform navigation.
/// </summary>

View file

@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using Xamarin.Forms;
@ -12,7 +12,7 @@ namespace TINK.View.Map
using TINK.ViewModel.Map;
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class MapPage : ContentPage, IViewService, IDetailPage
#else
public partial class MapPage : ContentPage, IViewService
@ -71,7 +71,7 @@ namespace TINK.View.Map
public new async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
=> await App.Current.MainPage.DisplayAlert(title, message, accept, cancel);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
@ -97,7 +97,7 @@ namespace TINK.View.Map
/// <param name="typeOfPage">Page to display.</param>
public async Task PushAsync(ViewTypes typeOfPage)
{
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
var page = Activator.CreateInstance(typeOfPage.GetViewType()) as IDetailPage;
#else
var page = Activator.CreateInstance(p_oTypeOfPage.GetViewType());
@ -107,7 +107,7 @@ namespace TINK.View.Map
return;
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
page.NavigationMasterDetail = NavigationMasterDetail;
#endif
@ -120,7 +120,7 @@ namespace TINK.View.Map
public Task<IUserFeedback> DisplayUserFeedbackPopup() => throw new NotSupportedException();
#endif
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary> Delegate to perform navigation.</summary>
public INavigationMasterDetail NavigationMasterDetail { private get; set; }
@ -134,7 +134,7 @@ namespace TINK.View.Map
// Pass reference to member Navigation to show bikes at station x dialog.
try
{
Log.ForContext<MainPage>().Verbose("Constructing map page view model.");
Log.ForContext<MapPage>().Verbose("Constructing map page view model.");
#if TRYNOTBACKSTYLE
MapPageViewModel = new MapPageViewModel();
@ -151,7 +151,7 @@ namespace TINK.View.Map
} catch (Exception exception)
{
Log.ForContext<MainPage>().Error("Constructing map page view model failed. {Exception}", exception);
Log.ForContext<MapPage>().Error("Constructing map page view model failed. {Exception}", exception);
return;
}
@ -159,13 +159,13 @@ namespace TINK.View.Map
{
BindingContext = MapPageViewModel;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
MapPageViewModel.NavigationMasterDetail = NavigationMasterDetail;
#endif
}
catch (Exception exception)
{
Log.ForContext<MainPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
Log.ForContext<MapPage>().Error("Setting binding/ navigaton on map page failed. {Exception}", exception);
return;
}
@ -184,7 +184,7 @@ namespace TINK.View.Map
catch (Exception exception)
{
// Continue because styling is not essential.
Log.ForContext<MainPage>().Error("IOS specific styling of map page failed. {Exception}", exception);
Log.ForContext<MapPage>().Error("IOS specific styling of map page failed. {Exception}", exception);
}
try
@ -194,14 +194,14 @@ namespace TINK.View.Map
catch (Exception exception)
{
// Continue because styling is not essential.
Log.ForContext<MainPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
Log.ForContext<MapPage>().Error("Invoking OnAppearing of base failed. {Exception}", exception);
return;
}
try
{
// Pre move and scanle maps to avoid initial display of map in Rome.
Log.ForContext<MainPage>().Verbose("Moving and scaling map.");
Log.ForContext<MapPage>().Verbose("Moving and scaling map.");
MapPageViewModel.MoveAndScale(
(mapSpan) => MyMap.MoveToRegion(mapSpan),
App.ModelRoot.Uris.ActiveUri,
@ -210,17 +210,17 @@ namespace TINK.View.Map
catch(Exception exception)
{
// Continue because a map not beeing moved/ scaled is no reason for aborting startup.
Log.ForContext<MainPage>().Error("Moving and scaling map failed. {Exception}", exception);
Log.ForContext<MapPage>().Error("Moving and scaling map failed. {Exception}", exception);
}
try
{
Log.ForContext<MainPage>().Verbose("Invoking OnAppearing on map page view model.");
Log.ForContext<MapPage>().Verbose("Invoking OnAppearing on map page view model.");
await MapPageViewModel.OnAppearing();
}
catch (Exception exception)
{
Log.ForContext<MainPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
Log.ForContext<MapPage>().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception);
return;
}
}

View file

@ -66,7 +66,7 @@ namespace TINK.View.MiniSurvey
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);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> throw new NotImplementedException();
#else

View file

@ -2,7 +2,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using Xamarin.Forms;
@ -138,7 +138,7 @@ namespace TINK.View.MyBikes
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);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> throw new NotImplementedException();
#else

View file

@ -1,6 +1,6 @@
using System;
using TINK.Model.Station;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using Xamarin.Forms;
@ -18,7 +18,7 @@ namespace TINK.View.Root
// - switch to login page form bikes at station page if not yet logged in
/// </remarks>
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class RootPage : FlyoutPage, INavigationMasterDetail
#else
public partial class RootPage : FlyoutPage
@ -38,7 +38,7 @@ namespace TINK.View.Root
return;
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
var detailPage = navigationPage.RootPage as IDetailPage;
if (detailPage == null)
{
@ -80,7 +80,7 @@ namespace TINK.View.Root
var page = (Page)Activator.CreateInstance(typeOfPage);
page.Title = title;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
if (page is IDetailPage detailPage)
{
// Detail page needs reference to perform navigation.

View file

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

View file

@ -1,17 +0,0 @@
<?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

@ -1,91 +0,0 @@
using System;
using TINK.View.Info.BikeInfo;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.ViewModel.MasterDetail;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.View
{
/// <summary>
/// Delegate to perform navigation.
/// </summary>
public delegate void ShowPageDelegate(Type p_oType, string p_strTitle = null);
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
public partial class MainPage : MasterDetailPage, INavigationMasterDetail
#else
public partial class MainPage : MasterDetailPage
#endif
{
public MainPage()
{
InitializeComponent();
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;
}
#if USEMASTERDETAIL || USEFLYOUT
var detailPage = navigationPage.RootPage as IDetailPage;
if (detailPage == null)
{
return;
}
detailPage.NavigationMasterDetail = this;
#endif
}
/// <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);
#if USEMASTERDETAIL || USEFLYOUT
var l_oPage = page as IDetailPage;
if (l_oPage != null)
{
l_oPage.NavigationMasterDetail = this;
}
#endif
#if !BACKSTYLE
// When bike info page is shown do not allow to close this carousel before all pages were displayed.
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

@ -1,51 +0,0 @@
<?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

@ -1,32 +0,0 @@
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

@ -2,7 +2,7 @@
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Threading.Tasks;
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using System;
@ -12,7 +12,7 @@ using Xamarin.CommunityToolkit.Extensions;
namespace TINK.View.Settings
{
[XamlCompilation(XamlCompilationOptions.Compile)]
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public partial class SettingsPage : ContentPage, IViewService, IDetailPage
#else
public partial class SettingsPage : ContentPage, IViewService
@ -88,7 +88,7 @@ namespace TINK.View.Settings
public new async Task<string> DisplayActionSheet(String p_strTitle, String p_strCancel, String destruction, params String[] p_oButtons)
=> await base.DisplayActionSheet(p_strTitle, p_strCancel, destruction, p_oButtons);
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>
/// Creates and a page an shows it.
/// </summary>
@ -110,7 +110,7 @@ namespace TINK.View.Settings
public Task PopModalAsync()
=> throw new NotSupportedException();
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
/// <summary>Delegate to perform navigation.</summary>
private INavigationMasterDetail m_oNavigation;

View file

@ -63,7 +63,7 @@ namespace TINK.View.WhatsNew.Agb
public Task PushModalAsync(ViewTypes p_oTypeOfPage)
=> throw new NotImplementedException();
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> throw new NotImplementedException();
#else

View file

@ -67,7 +67,7 @@ namespace TINK.View.WhatsNew
await Navigation.PushModalAsync((Page)Activator.CreateInstance(p_oTypeOfPage.GetViewType()));
}
#if USEMASTERDETAIL || USEFLYOUT
#if USEFLYOUT
public void ShowPage(ViewTypes p_oType, string p_strTitle = null)
=> throw new NotImplementedException();
#else

View file

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

View file

@ -1,221 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using TINK.Services;
using TINK.Services.CopriApi.ServerUris;
using TINK.View;
using TINK.View.Account;
using TINK.View.Contact;
using TINK.View.FindBike;
using TINK.View.Info;
using TINK.View.Login;
using TINK.View.Map;
using TINK.View.MyBikes;
using TINK.View.Settings;
using TINK.View.Themes;
using Xamarin.Forms;
namespace TINK.ViewModel
{
/// <summary>
/// View model managing master detail menu.
/// </summary>
class MasterPageViewModel : INotifyPropertyChanged
{
/// <summary>
/// Dictionary to manage collection of menu items.
/// </summary>
private Dictionary<Type, int> m_oEntryDictionary;
/// <summary> Updates menu entries depending on state. </summary>
private void UpdateMenuEntries()
{
var l_oSelectedMenuItem = SelectedMenuItem ?? new MainPageMenuItem(0, typeof(MapPage));
for (int l_iIndex = MenuItems.Count - 1; l_iIndex >= 0; l_iIndex--)
{
MenuItems.Clear();
m_oEntryDictionary.Clear();
}
CheckAddItem(typeof(MapPage));
if (App.ModelRoot.ActiveUser.IsLoggedIn)
{
CheckAddItem(typeof(MyBikesPage));
CheckAddItem(typeof(FindBikePage));
CheckAddItem(typeof(AccountPage));
}
else
{
CheckAddItem(typeof(LoginPage));
}
if (App.ModelRoot.Uris.ActiveUri.Host.GetIsCopri()
|| App.ModelRoot.ActiveUser.IsLoggedIn)
{
CheckAddItem(typeof(SettingsPage));
}
CheckAddItem(typeof(FeesAndBikesPage)); // Fees and bikes
CheckAddItem(typeof(ContactPage)); // Feedback and contact
CheckAddItem(typeof(TabbedPageInfo)); // About sharee.bike
}
/// <summary>Adds a menu item to master detail menu.</summary>
/// <param name="p_oType">Type decribing entry to be added.</param>
private void CheckAddItem(Type p_oType)
{
if (m_oEntryDictionary.ContainsKey(p_oType))
{
// Nothing to do because has already been added.
return;
}
m_oEntryDictionary.Add(
p_oType,
m_oEntryDictionary.Count);
MenuItems.Add(
new MainPageMenuItem(m_oEntryDictionary[p_oType],
p_oType));
}
/// <summary> Removes a page from menu.</summary>
/// <param name="p_oType"></param>
private void RemoveItem(Type p_oType)
{
if (!m_oEntryDictionary.ContainsKey(p_oType))
{
// Nothing to do because item was never added/ already removed.
return;
}
MenuItems.Remove(MenuItems[m_oEntryDictionary[p_oType]]);
m_oEntryDictionary.Remove(p_oType);
}
public MasterPageViewModel()
{
MenuItems = new ObservableCollection<MainPageMenuItem>();
m_oEntryDictionary = new Dictionary<Type, int>();
App.ModelRoot.ActiveUser.StateChanged += (sender, eventargs) => OnUpdateRequired();
// Update flyout view model whenever theme is switched.
App.ModelRoot.Themes.PropertyChanged += (sender, eventargs) =>
{
if (!(sender is ServicesContainerMutable<object> themes))
return;
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(themes.Active);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
};
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(App.ModelRoot.Themes.Active);
UpdateMenuEntries();
}
/// <summary>
/// Gets the flyout title from theme name
/// </summary>
/// <param name="theme">Name of theme.</param>
/// <returns>Flyout title.</returns>
private string GetMasterDetailMenuTitle(object theme)
{
if (!(theme is ITheme active))
return "sharee.bike";
return $"sharee.bike{(!string.IsNullOrEmpty(active.OperatorInfo) ? ($"\r\n{active.OperatorInfo}") : string.Empty)}";
}
private string masterDetailMenuTitlte;
/// <summary>
/// Holds the title of the fylout page.
/// </summary>
public string MasterDetailMenuTitlte
{
get
{
return masterDetailMenuTitlte;
}
set
{
if (masterDetailMenuTitlte == value)
return;
masterDetailMenuTitlte = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
}
}
/// <summary>
/// Provides collection of menu items for master page.
/// </summary>
public ObservableCollection<MainPageMenuItem> MenuItems
{
get; private set;
}
/// <summary>
/// View service used to manage menu state.
/// </summary>
public ShowPageDelegate ShowPageViewService { get; set; }
/// <summary>
/// Gets or sets the selected item.
/// </summary>
public MainPageMenuItem SelectedMenuItem
{
get;
set;
}
/// <summary>
/// Command object which is invoked from view when an item is selected.
/// </summary>
public Command MenuItemSelected
{
get
{
return new Command(OnUpdateRequired);
}
}
/// <summary> Manges updates required due to selection of a menu or login events.</summary>
public void OnUpdateRequired()
{
if (ShowPageViewService == null)
return;
UpdateMenuEntries();
if (SelectedMenuItem == null)
{
// Nothing to do if no menu entry is selected.
return;
}
ShowPageViewService(SelectedMenuItem.TargetType);
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged == null)
return;
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}