sharee.bike-App/LastenradBayern/ShareeBike/App.xaml.cs

348 lines
12 KiB
C#
Raw Normal View History

2024-04-09 12:53:23 +02:00
using System;
2021-11-07 19:42:59 +01:00
using System.Collections.Generic;
2022-08-30 15:42:25 +02:00
using System.Globalization;
2021-11-07 19:42:59 +01:00
using System.Linq;
2022-08-30 15:42:25 +02:00
using System.Threading;
using System.Threading.Tasks;
2021-11-07 19:42:59 +01:00
using MonkeyCache.FileStore;
using Plugin.Connectivity;
2022-08-30 15:42:25 +02:00
using Serilog;
using Serilog.Core;
using Serilog.Events;
2024-04-09 12:53:23 +02:00
using ShareeBike.Model;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Device;
using ShareeBike.Model.Logging;
using ShareeBike.Model.Settings;
using ShareeBike.Model.User.Account;
using ShareeBike.Services;
using ShareeBike.Services.BluetoothLock.Crypto;
using ShareeBike.Services.Geolocation;
using ShareeBike.Services.Permissions;
using ShareeBike.View;
2022-08-30 15:42:25 +02:00
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
2021-11-07 19:42:59 +01:00
// Required for support of binding package, see https://github.com/nuitsjp/Xamarin.Forms.GoogleMaps.Bindings.
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
2023-05-11 17:39:28 +02:00
// Add ExportFont attribute
[assembly: ExportFont("Font Awesome 5 Free-Solid-900.otf", Alias = "FA-S")]
2024-04-09 12:53:23 +02:00
[assembly: ExportFont("Font Awesome 5 Brands-Regular-400.otf", Alias = "FA-B")]
[assembly: ExportFont("Font Awesome 5 Free-Regular-400.otf", Alias = "FA-R")]
[assembly: ExportFont("Roboto-Regular.ttf", Alias = "RobotoRegular")]
[assembly: ExportFont("Roboto-Bold.ttf", Alias = "RobotoBold")]
[assembly: ExportFont("Roboto-Italic.ttf", Alias = "RobotoItalic")]
2023-05-11 17:39:28 +02:00
2024-04-09 12:53:23 +02:00
namespace ShareeBike
2021-11-07 19:42:59 +01:00
{
2022-09-06 16:08:19 +02:00
public partial class App : Application
{
/// <summary>Title of the attachment file.</summary>
private const string ATTACHMENTTITLE = "Diagnostics.txt";
/// <summary> Model root. </summary>
2024-04-09 12:53:23 +02:00
private static ShareeBikeApp m_oModelRoot;
2022-09-06 16:08:19 +02:00
/// <summary>
/// Gets the model root.
/// </summary>
2024-04-09 12:53:23 +02:00
public static ShareeBikeApp ModelRoot
2022-09-06 16:08:19 +02:00
{
get
{
if (m_oModelRoot != null)
{
// Root model already exists, nothing to do.
return m_oModelRoot;
}
// Get folder where to read settings from
var specialFolders = DependencyService.Get<ISpecialFolder>();
var internalPersonalDir = specialFolders.GetInternalPersonalDir();
2023-05-09 08:47:52 +02:00
// Delete attachment from previous session.
2022-09-06 16:08:19 +02:00
DeleteAttachment(internalPersonalDir);
// Setup logger using default settings.
2024-04-09 12:53:23 +02:00
ShareeBikeApp.SetupLogging(
2022-09-06 16:08:19 +02:00
new LoggingLevelSwitch(Model.Settings.Settings.DEFAULTLOGGINLEVEL),
internalPersonalDir);
// Subscribe to any unhandled/ unobserved exceptions.
AppDomain.CurrentDomain.UnhandledException += (sender, unobservedTaskExceptionEventArgs) => { Log.Fatal("Unobserved task exception: {Exception}", unobservedTaskExceptionEventArgs.ExceptionObject); };
TaskScheduler.UnobservedTaskException += (sender, unhandledExceptionEventArgs) => { Log.Fatal("Unhandled exception: {Exception}", unhandledExceptionEventArgs.Exception); };
// Restore last model state from json- file.
Dictionary<string, string> settingsJSON = new Dictionary<string, string>();
try
{
settingsJSON = JsonSettingsDictionary.Deserialize(internalPersonalDir);
}
catch (Exception exception)
{
Log.Error("Reading application settings from file failed.", exception);
}
Model.Settings.Settings settings;
try
{
settings = new Model.Settings.Settings(
null, // Turn off filtering for LastenradBayern- context
null, // Turn off filtering for LastenradBayern- context
2022-10-17 18:45:38 +02:00
JsonSettingsDictionary.GetStartupSettings(settingsJSON) ?? new StartupSettings(),
2022-09-06 16:08:19 +02:00
JsonSettingsDictionary.GetCopriHostUri(settingsJSON),
JsonSettingsDictionary.GetPollingParameters(settingsJSON),
JsonSettingsDictionary.GetMinimumLoggingLevel(settingsJSON),
JsonSettingsDictionary.GetIsReportLevelVerbose(settingsJSON),
JsonSettingsDictionary.GetExpiresAfter(settingsJSON),
JsonSettingsDictionary.GetActiveLockService(settingsJSON),
JsonSettingsDictionary.GetConnectTimeout(settingsJSON),
JsonSettingsDictionary.GetActiveGeolocationService(settingsJSON),
JsonSettingsDictionary.GetCenterMapToCurrentLocation(settingsJSON),
2024-04-09 12:53:23 +02:00
Xamarin.Forms.GoogleMaps.MapSpan.FromCenterAndRadius(new Xamarin.Forms.GoogleMaps.Position(48.945396, 11.395330), Xamarin.Forms.GoogleMaps.Distance.FromKilometers(2.9)),
2022-09-06 16:08:19 +02:00
JsonSettingsDictionary.GetLogToExternalFolder(settingsJSON),
JsonSettingsDictionary.GetIsSiteCachingOn(settingsJSON),
JsonSettingsDictionary.GetActiveTheme(settingsJSON) ?? typeof(Themes.LastenradBayern).Name);
}
catch (Exception exception)
{
Log.Error("Deserializing application settings from dictionary failed.", exception);
settings = new Model.Settings.Settings();
}
if (settings.MinimumLogEventLevel != Model.Settings.Settings.DEFAULTLOGGINLEVEL
|| settings.LogToExternalFolder)
{
2023-05-09 08:47:52 +02:00
// Either
2022-09-06 16:08:19 +02:00
// - logging is not set to default value or
// - logging is performed to external folder.
// Need to reconfigure.
2023-05-09 08:47:52 +02:00
Log.CloseAndFlush(); // Close before modifying logger configuration. Otherwise a sharing violation occurs.
2022-09-06 16:08:19 +02:00
2024-04-09 12:53:23 +02:00
ShareeBikeApp.SetupLogging(
2022-09-06 16:08:19 +02:00
new LoggingLevelSwitch(settings.MinimumLogEventLevel),
!settings.LogToExternalFolder
? internalPersonalDir
: specialFolders.GetExternalFilesDir());
}
// Get auth cookie
Log.Debug("Get auth cookie.");
IStore store = null;
2023-02-22 14:03:35 +01:00
// Version of last version used or null for initial installation.
// Used for updating purposes.
2022-09-06 16:08:19 +02:00
var lastVersion = JsonSettingsDictionary.GetAppVersion(settingsJSON);
if (new Version(3, 0, 290) <= lastVersion)
{
// App versions newer than 3.0.173 stored geolocation service in configuration.
// Version 3.0.290: Geolocation service "GeolocationService" is no more supported.
2023-05-09 08:47:52 +02:00
// For this reasons a switch of geolocation service is forced when loading configurations from ealier versions.
2022-09-06 16:08:19 +02:00
LocationServicesContainer.SetActive(settings.ActiveGeolocationService);
}
store = new Store();
Barrel.ApplicationId = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
2023-02-22 14:03:35 +01:00
// Get main thread synchronization context to be able to update gui elements from worker threads.
2022-09-06 16:08:19 +02:00
var context = SynchronizationContext.Current;
var appInfoService = DependencyService.Get<IAppInfo>();
2023-02-22 14:03:35 +01:00
var smartDevice = DependencyService.Get<ISmartDevice>();
2022-09-06 16:08:19 +02:00
const string MERCHANTID = "0000000000";
2023-05-09 08:47:52 +02:00
// Create new app instance.
2022-09-06 16:08:19 +02:00
Log.Debug("Constructing main model...");
2024-04-09 12:53:23 +02:00
m_oModelRoot = new ShareeBikeApp(
2022-09-06 16:08:19 +02:00
settings,
store, // Manages user account
isConnectedFunc: () => CrossConnectivity.Current.IsConnected,
connectorFactory: (isConnected, activeUri, sessionCookie, mail, expiresAfter) => ConnectorFactory.Create(
isConnected,
activeUri,
new Repository.AppContextInfo(MERCHANTID, AppFlavor.LastenradBayern.GetDisplayName().Replace(" ", ""), appInfoService.Version),
CultureInfo.CurrentUICulture.TwoLetterISOLanguageName,
sessionCookie,
mail,
2023-02-22 14:03:35 +01:00
smartDevice,
2022-09-06 16:08:19 +02:00
expiresAfter),
merchantId: MERCHANTID,
bluetoothService: BluetoothService, /* locksService */
locationPermissionsService: PermissionsService,
locationServicesContainer: LocationServicesContainer,
locksService: null,
2023-02-22 14:03:35 +01:00
device: smartDevice,
2022-09-06 16:08:19 +02:00
specialFolder: specialFolders,
cipher: new Cipher(),
2024-04-09 12:53:23 +02:00
new ShareeBike.Services.ThemeNS.Theme(Application.Current.Resources.MergedDictionaries),
2022-09-06 16:08:19 +02:00
postAction: (d, obj) => context.Post(d, obj),
currentVersion: appInfoService.Version,
lastVersion: lastVersion,
whatsNewShownInVersion: JsonSettingsDictionary.GetWhatsNew(settingsJSON) ?? settingsJSON.GetAppVersion(),
flavor: AppFlavor.LastenradBayern);
Log.Debug("Main model successfully constructed.");
return m_oModelRoot;
}
}
/// <summary>
/// Entry point of application.
/// </summary>
public App()
{
InitializeComponent();
2021-11-07 19:42:59 +01:00
2022-10-17 18:45:38 +02:00
// Check which page to show first.
var mainPage = new View.RootShell.AppShell();
2024-04-09 12:53:23 +02:00
var currentItem = Activator.CreateInstance(ModelRoot.StartupSettings.StartupPage.GetViewType()) as ContentPage;
if (currentItem != null && ModelRoot.ActiveUser.IsLoggedIn)
{
mainPage.CurrentItem = currentItem;
}
2021-11-07 19:42:59 +01:00
2022-10-17 18:45:38 +02:00
MainPage = ModelRoot.WhatsNew.IsShowRequired
? new View.WhatsNew.WhatsNewPage(() => MainPage = mainPage) // Show whats new info.
: (Page)mainPage; // Just start sharee- app
2022-09-06 16:08:19 +02:00
}
/// <summary> Concatenates all log files to a single one. </summary>
/// <returns>Full file name of attachment.</returns>
public static string CreateAttachment()
{
var sessionLogFiles = Log.Logger.GetLogFiles().ToArray();
if (sessionLogFiles.Length < 1)
{
// Either
// - there is no logging file
// - an error occurred getting list of log files.
return string.Empty;
}
var fullLogFileName = System.IO.Path.Combine(ModelRoot.LogFileParentFolder, ATTACHMENTTITLE);
// Stop logging to avoid file access exception.
Log.CloseAndFlush();
System.IO.File.WriteAllLines(
fullLogFileName,
sessionLogFiles.SelectMany(name =>
(new List<string> { $"{{\"SessionFileName\":\"{name}\"}}" })
.Concat(System.IO.File.ReadLines(name).ToArray())));
// Resume logging
2024-04-09 12:53:23 +02:00
ShareeBikeApp.SetupLogging(
2022-09-06 16:08:19 +02:00
ModelRoot.Level,
ModelRoot.LogFileParentFolder);
return fullLogFileName;
}
/// <summary>Deletes an attachment if there is one.</summary>
/// <param name="folder">Folder to delete, is null folder is queried from model.</param>
private static void DeleteAttachment(string folder = null)
{
var attachment = System.IO.Path.Combine(folder ?? ModelRoot.LogFileParentFolder, ATTACHMENTTITLE);
if (!System.IO.File.Exists(attachment))
{
// No attachment found.
return;
}
System.IO.File.Delete(attachment);
}
protected override void OnSleep()
{
// Handle when your app sleeps
Log.CloseAndFlush();
}
protected override void OnResume()
{
DeleteAttachment();
2024-04-09 12:53:23 +02:00
ShareeBikeApp.SetupLogging(
2022-09-06 16:08:19 +02:00
ModelRoot.Level,
ModelRoot.LogFileParentFolder);
}
/// <param name="uri">The URI for the request.</param>
/// <summary>Overriden to respond when the user initiates an app link request.</summary>
protected override void OnAppLinkRequestReceived(Uri uri)
{
base.OnAppLinkRequestReceived(uri);
if (uri.Host.ToLower() == "sharee.bike")
{
// Input e.g. sharee.bike/sharee?lat=49.921&long=32.51
Array segments = Array.ConvertAll(uri.Segments, segment => segment.Replace("/", "")).Skip(1).ToArray();
if (uri.Query.Length > 0)
{
Dictionary<string, string> queryDict = uri.Query
.Substring(1)
.Split("&")
.Select(query => query.Split('='))
.ToDictionary(query => query.FirstOrDefault(), query => query.Skip(1).FirstOrDefault());
}
// segments == ["sharee"]
// queryDict == [{["lat", "49.921"]}], {["long", "32.51"]}]
// => Navigate and pass params depending on linkinput
// If no custom navigation is configured, the app just opens as if the user opened it
}
}
/// <summary> Gets the current logging level.</summary>
/// <returns></returns>
private static LogEventLevel GetCurrentLogEventLevel()
{
foreach (LogEventLevel level in Enum.GetValues(typeof(LogEventLevel)))
{
if (Log.IsEnabled(level))
return level;
}
return LogEventLevel.Error;
}
/// <summary>
/// Holds the permission service instance.
/// </summary>
private static ILocationPermission _PermissionsService = null;
/// <summary>
/// Service to manage permissions (location) of the app.
/// </summary>
public static ILocationPermission PermissionsService
{
get
{
if (_PermissionsService != null)
return _PermissionsService;
2024-04-09 12:53:23 +02:00
_PermissionsService = new ShareeBike.Services.Permissions.Essentials.LocationPermissions();
2022-09-06 16:08:19 +02:00
return _PermissionsService;
}
}
/// <summary> Service to manage bluetooth stack.</summary>
public static Plugin.BLE.Abstractions.Contracts.IBluetoothLE BluetoothService => Plugin.BLE.CrossBluetoothLE.Current;
/// <summary>
/// Service container to manage geolocation services.
/// </summary>
2023-04-05 15:02:10 +02:00
public static IServicesContainer<IGeolocationService> LocationServicesContainer { get; }
= new ServicesContainerMutableT<IGeolocationService>(
new HashSet<IGeolocationService> {
2022-09-06 16:08:19 +02:00
new LastKnownGeolocationService(DependencyService.Get<IGeolodationDependent>()),
new SimulatedGeolocationService(DependencyService.Get<IGeolodationDependent>()),
new GeolocationAccuracyMediumService(DependencyService.Get<IGeolodationDependent>()),
new GeolocationAccuracyHighService(DependencyService.Get<IGeolodationDependent>()),
new GeolocationAccuracyBestService(DependencyService.Get<IGeolodationDependent>())},
Model.Settings.Settings.DefaultLocationService.FullName);
}
2021-11-07 19:42:59 +01:00
}